PHP - Deleting duplicate values with same ref - php

I've the following MySQL Table called store
id ref item_no supplier
1 10 x1 usa
2 10 x1 usa
3 11 x1 china
4 12 x2 uk
5 12 x3 uk
6 13 x3 uk
7 13 x3 uk
Now What i'm excepting the output to be is as follows :
id ref item_no supplier
1 10 x1 usa
3 11 x1 china
4 12 x2 uk
5 12 x3 uk
6 13 x3 uk
As you can see item_no x1 and x3 have same ref and supplier source, so what I want is to delete the duplicate record in-order to keep one item_no only !
I've create this PHP code to SELECT results only :
$query1 = "SELECT
DISTINCT(item_no) AS field,
COUNT(item_no) AS fieldCount,
COUNT(ref) AS refcount
FROM
store
GROUP BY item_no HAVING fieldCount > 1";
$result1 = mysql_query($query1);
if(mysql_num_rows($result1)>0){
while ($row1=mysql_fetch_assoc($result1)) {
echo $row1['field']."<br /><br />";
}
} else {
//IGNORE
}
How to tell the query to SELECT Duplicate records properly according to my needs before creating the DELETE query.
Thanks Guys

You can use the following query to produce the required result set:
SELECT t1.*
FROM store AS t1
JOIN (
SELECT MIN(id) AS id, ref, item_no
FROM store
GROUP BY ref, item_no
) AS t2 ON t1.id > t2.id AND t1.ref = t2.ref AND t1.item_no = t2.item_no
Demo here
To DELETE you can use:
DELETE t1
FROM store AS t1
JOIN (
SELECT MIN(id) AS id, ref, item_no
FROM store
GROUP BY ref, item_no
) AS t2 ON t1.id > t2.id AND t1.ref = t2.ref AND t1.item_no = t2.item_no

To find only duplicate records you can use
SELECT * FROM store WHERE id NOT IN
(SELECT id FROM store AS outerStore WHERE id =
(SELECT MAX(id) FROM store AS innerStore
WHERE outerStore.ref = innerStore.ref AND
outerStore.supplier = innerStore.supplier AND outerStore.item_no = innerStore.item_no))
Maybe long, but it should work.

If you want the select of the row to delete use
select * from store
where id not in (
select max(id) from store
group by distinct ref, item_no, supplier);
Or you can directly use a command for direct delete using
delete from store
where id not in (
select max(id) from store
group by distinct ref, item_no, supplier);

Related

Mysql query help to get users count from table area wise

I have a table which is having some report info. In that table I have userid and areaname .
Now my requirement is to get the count of user's list based on area
For example table will have
userid | areaname
-----------------
1 |area 1
1 |area 1
2 |area2
2 |area 2
2 |area2
3 |area1
3 |area1
4 |area3
5 |area2
---------------
Result must be
area1 2users
area2 2users
area3 1user
what is the mysql query to achieve this?
Use the Aggregate function COUNT() to get the number of users and GROUP BY to get the count based on areaname
Use DISTINCT for unique values
SELECT COUNT(DISTINCT userid),areaname FROM tablename GROUP BY areaname;
SELECT count(userid),area FROM YOURTABLENAME GROUP BY area;
I see, you can have dublicate entries ad well:
So I would make a sub Select
SELECT sum(partly_sum), area
FROM (
SELECT
userid, area , count(*) as partly_sum
FROM
_YOUR_TABLE_NAME_
WHERE 1
GROUP BY area, userid
) as a_bad_sub_query
WHERE 1
GROUP BY area
Regards
You can use distinct to avoid dupe entries
SELECT COUNT(DISTINCT userid),areaname FROM tablename GROUP BY areaname

Fetch data from mapping table if the condition matches in multiple rows

product_id property_id
1 2
1 5
2 2
3 5
I have a mapping table as above. I want to get only product with id =1 if product_id in (2,5). i.e. I want to fetch data if the table contains both 2,5 not the data if it is with property_id only 2 or 5
select group_concat(distinct product_id) product_ids from table where property_id in (2,5)
UPDATE:
The property_id in can be property_id in(2,5,....). I get output from form as 2,5,.... and so on. Its not just for the single case. I just want the output if the condition in property_id in matches the whole series.
This how it could be done
select
product_id from
table_name
where property_id in (2,5)
group by product_id
having count(*) = 2
All you need to change having count(*) = 2 to the number of items inside IN() , right now its 2 and if you are looking at 3 property id then it will be 3 and so on.
select distinct a.product_id
from table a, table b
where a.product_id = b.product_id
and a.property_id = 2
and b.property_id = 5

Calculate net change in ranking after "update", MYSQL

I have a two MYSQL tables:
Table-1
id catid title user_rating
123 8 title-one 3
321 8 title-two 5
and
Table-2
listing_id title user_rating
123 title-one 3
321 title-two 5
Plus, I have this query that calculates the current rank of each "title" based on "user_rating".
SELECT
MAX(x.rank) AS rank
FROM
(SELECT
a.id,
a.catid,
a.title,
b.listing_id,
#rank:=#rank + 1 AS rank
FROM
`table-1` a
INNER JOIN `table-2` b ON a.id = b.listing_id
CROSS JOIN (SELECT #rank:=0) r
WHERE
catid = '8'
ORDER BY user_rating DESC) x
WHERE
id = 123
Now, my issue: I want to calculate the difference in "ranking" (rank) when I update the "user_rating" value.
Please, note: the "user_rating" value is updated by a php script that allow users to vote for a specific content (range 1 to 5, step 0.5).
What's the best way to get the difference between the "previous rank" and "current rank" after the update?
Thanks in advance to all.

PDO select and count IN HAVING DISTINCT to appear in dropdown

I have this mysql select working great. It returns the proper data. I can't seem to get the context correct to place the actual count of the custnum so it will appear on the end of the dropdown option select.
This statement returns the proper location names
$statement = $pdo->prepare("SELECT locationname FROM location WHERE locationname IN (SELECT locationname FROM location_user WHERE custnum= :custnum GROUP BY locationname HAVING COUNT( DISTINCT email) < 6 )");
$statement->execute(array(':custnum' => $session->custnum));
while($row = $statement->fetch(PDO::FETCH_ASSOC)){
echo'<option value="'.$row['locationname'].'">'.$row['locationname'].'('. $row['COUNT(total)'] .')</option>';
}
Here's one of my attempts to grab the total for each custnum
$statement = $pdo->prepare("SELECT locationname, COUNT(custnum) AS total FROM location WHERE locationname IN (SELECT locationname FROM location_user WHERE custnum= :custnum GROUP BY locationname HAVING COUNT( DISTINCT email) < 6 )");
$statement->execute(array(':custnum' => $session->custnum));
while($row = $statement->fetch(PDO::FETCH_ASSOC)){
echo'<option value="'.$row['locationname'].'">'.$row['locationname'].'('. $row['total'] .')</option>';
}
Here's my tables
table location table location_user
custnum | locationname custnum | locationname | email | userlevel
1 location1 1 location1 1me#you.com 3
1 location2 1 location1 1me#you.com 1
1 location1 2me#you.com 2
1 location1 3me#you.com 2
1 location1 4me#you.com 2
1 location1 5me#you.com 2
1 location2 1me#you.com 2
1 location2 1me#you.com 3
The first select returns
location1()
location2()
The second select returns
location1(2)
I actually need the count of the distinct email which the query is doing and returning only the locationnames of the distinct email in the table less then 6 but how do I get the actual number of distinct emails for each locationname.
This select will retrieve the total for DISTINCT email, but how do I combine the two into one for my while loop?
$statement2 = $pdo->prepare("SELECT COUNT(email) AS total FROM location_user WHERE custnum= :custnum GROUP BY locationname HAVING COUNT( DISTINCT email) < 6");
$statement2->execute(array(':custnum' => $session->custnum));
Here's the working version from the help of Peter and a little prodding from Tin.
$statement = $pdo->prepare("SELECT l.locationname, COUNT(DISTINCT lu.email) AS total
FROM location l LEFT JOIN location_user lu ON l.locationname = lu.locationname AND l.custnum = lu.custnum WHERE l.custnum = :custnum GROUP BY l.locationname HAVING COUNT(DISTINCT lu.email) < 5 ");
$statement->execute(array(':custnum' => $session->custnum));
while($row = $statement->fetch(PDO::FETCH_ASSOC)){
echo'<option value="'.$row['locationname'].'">'.$row['locationname'].'('. $row['total'] .')</option>';
}
Here's another version that I'm working on to skip the user that adds the locations to the table. This user will always have a userlevel > 2. The uselevel is placed in the location_user table only as a value between 1-9. So I still need the location name but I don't want their location included in the count. I just realized that I could actually go a better route because the only email that I want to count will have a userlevel of 2. I was using the distinct email to filter out the userlevel of 1. I'll give it a go. The below version drops my locations that arn't in the location_user table but it's returning the proper count.
SELECT l.locationname, COUNT(lu.userlevel) AS total
FROM location l LEFT JOIN location_user lu
ON l.locationname = lu.locationname
AND l.custnum = lu.custnum
WHERE l.custnum = :custnum
AND lu.userlevel = 2
GROUP BY l.locationname
HAVING COUNT(lu.userlevel) < 6
UPDATE2: based on your comments. Try it this way
SELECT l.locationname, COUNT(DISTINCT lu.email) AS total
FROM location l LEFT JOIN location_user lu
ON l.locationname = lu.locationname
AND l.custnum = lu.custnum
AND lu.userlevel < 3 -- consider only users with user level < 3
WHERE l.custnum = ?
GROUP BY l.locationname
HAVING COUNT(DISTINCT lu.email) < 6
Sample output:
| LOCATIONNAME | TOTAL |
|--------------|-------|
| location1 | 5 |
| location2 | 1 |
| location3 | 0 |
Here is SQLFiddle demo
you don't actually need to query from table location since you already have locationname field from table location_user
SELECT locationname, count(DISTINCT email) as total FROM location_user WHERE custnum = :custnum GROUP BY locationname HAVING count(DISTINCT email) < 6

How to limit / offset query that returns several rows per unique ID?

My issue is that I need to paginate data from this query:
function search($search_term, $limit, $offset)
{
$id = $this->auth->get_user_id();
$query = $this->db->query("
SELECT user_id,
first_name,
cars_name,
cars_id
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
ORDER BY first_name ASC
");
$search_data = array();
foreach ($query->result() as $row) {
$search_data[$row->user_id]['name'] = $row->first_name;
$search_data[$row->user_id]['cars'][$row->cars_id] = array(
'cars_name' => $row->cars_name);
}
return $search_data;
}
A sample data table / query response would be:
1 JOE HONDA 123
1 JOE TOYOTA 124
2 MAC VW 125
2 MAC HONDA 126
2 MAC TESLA 127
3 STU SUBARU 128
3 STU KIA 129
-----------
Page 1
-----------
1 JOE HONDA 123
TOYOTA 124
2 MAC VW 125
HONDA 126
------------
Page 2
------------
3 STU SUBARU 128
KIA 129
If I enter a limit and offset at the end of MySQL query
...
LIMIT $limit
OFFSET $offset;
");
the limit and offset are applied to the total number of rows, not the the number of rows grouped by user.
I've tried using GROUP BY but was unable to make it work.
My goal is to make the query as above but LIMIT and OFFSET the query by a number of rows that counts users, not all rows.
Any ideas?
I don't see a way to do this in one query. My solution would be to get the count of unique ID's using a group by query with the same parameters:
SELECT COUNT(1) AS uid_count
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
GROUP BY user_profiles.user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
Then fetch the uid_countmysql_num_rows and use that to calculate pagination variables for the above query.
The solution really is to use a GROUP BY clause:
SELECT SQL_CALC_FOUND_ROWS
user_id,
first_name,
cars_name,
cars_id
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
GROUP BY user_id
ORDER BY first_name ASC
LIMIT 100
The order is important. GROUP BY first, then ORDER BY, and then OFFSET/LIMIT.
Notice the SQL_CALC_FOUND_ROWS up there? After the query has executed, if you want to get the total row count (including those who aren't returned because of the LIMIT clause), just use:
SELECT FOUND_ROWS() AS `count`
And fetch the count column.
However, like you said, the rows will collapse and you will lose some cars_name and cars_id values.
Another solution is to use GROUP_CONCAT, then split it in PHP:
SELECT
user_id,
first_name,
GROUP_CONCAT(cars_name SEPARATOR ','),
GROUP_CONCAT(cars_id SEPARATOR ','),
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
ORDER BY first_name ASC
LIMIT 100
This would give you something like:
1 JOE HONDA,TOYOTA 123,124
2 MAC VW,HONDA,TESLA 125,126,127
3 STU SUBARU,KIA 128,129
If you want to get a list like this
Page 1
----------------------
1 JOE HONDA 123
1 JOE TOYOTA 124
Page 2
----------------------
2 MAC VW 125
2 MAC HONDA 126
2 MAC TESLA 127
Page 3
----------------------
3 STU SUBARU 128
3 STU KIA 129
Forget about limit, do this instead:
A - First retrieve a list of user id's and insert that into a temp table
CREATE TEMPORARY TABLE `test`.`temp_user_ids` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`user_id` INTEGER UNSIGNED NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE = MEMORY
B - Next insert the relavant user_id's into the table.
INSERT INTO temp_user_ids
SELECT null, user_id
FROM user_profiles
LEFT JOIN cars
ON cars.id_fk = user_id
WHERE user_id NOT LIKE '$id'
AND activated = 1
AND banned = 0
AND first_name LIKE '%$search_term%'
ORDER BY user_id DESC /*insert in reverse order !*/
The lowest user_id is the last_insert_id in the temptable, and the temp_table
items are in sequential order.
C - Set the SQL #var #current_id to the last_insert_id in the temp_table.
SELECT #current_id:= LAST_INSERT_ID()
D - Next select relevant rows from the table, using only the user_id you want.
SELECT count(*) as row_count,
up.user_id,
first_name,
group_concat(cars_name) as car_names,
group_concat(cars_id) as car_ids,
FROM user_profiles up
LEFT JOIN cars
ON cars.id_fk = up.user_id
INNER JOIN temp_user_ids t
ON (t.user_id = up.user_id)
WHERE t.id = #current_id
GROUP BY up.user_id
ORDER BY cars.id
E - Now lower the #current_id
SELECT #current_id:= #current_id - 1;
F - And repeat step D and E until there's no more rows to be had.
The first field row_count tells you the number of rows aggregated in the fields
car_names and car_ids. You can separate these fields by using php's explode.

Categories