Find All but last duplicate records from MYSQL database - mysql

I have a mysql table with following records
-------------------------------
| ID | Name | Age | XXX | YYY |
-------------------------------
| 1 | aa | 12 | qqq | rr |
-------------------------------
| 2 | aa | 12 | ttt | pp |
-------------------------------
| 3 | bb | 13 | qhq | rr |
-------------------------------
| 4 | bb | 13 | pqq | tr |
-------------------------------
| 5 | bb | 13 | ql | jjn |
-------------------------------
My requirement is to retrieve all duplicate records with respect to first column apart from the last entry. Currently I tried to retrieve duplicates and that is working fine
SELECT Name, Age, XXX, YYY FROM list
INNER JOIN (SELECT Name
FROM list
GROUP BY Name
HAVING COUNT(Name) > 1) dup
ON list.Name = dup.Name;
Output is like
-------------------------------
| ID | Name | Age | XXX | YYY |
-------------------------------
| 1 | aa | 12 | qqq | rr |
-------------------------------
| 2 | aa | 12 | ttt | pp |
-------------------------------
| 3 | bb | 13 | qhq | rr |
-------------------------------
| 4 | bb | 13 | pqq | tr |
-------------------------------
| 5 | bb | 13 | ql | jjn |
-------------------------------
But I want to remove the last record from the output like
-------------------------------
| ID | Name | Age | XXX | YYY |
-------------------------------
| 1 | aa | 12 | qqq | rr |
-------------------------------
| 3 | bb | 13 | qhq | rr |
-------------------------------
| 4 | bb | 13 | pqq | tr |
-------------------------------
How can I achieve this?

I would create two new columns in COUNT and Row_number the result because
Get COUNT total number by name column to get the MAX rownumber.
use Row_number by name to get rownumber.
To write a condition exclude the last rownumber by name from total count
If your mysql version support window function you can try this code.
make Row_number and COUNT then remove greater Row_number by Name.
SELECT *
FROM (
select *,
Row_number() over(partition by Name order by ID) rn,
COUNT(*) over(partition by Name) totle
from `list`
) t1
where rn <> totle
sqlfiddle
EDIT
if you have ID auto_increment column and your mysql didn't support window function you can try this query.
SELECT id,Name,Age,XXX,YYY
FROM (
SELECT *,
(SELECT COUNT(*) FROM `list` t1 WHERE t.ID >= t1.ID AND t.Name = t1.Name) rn,
(SELECT COUNT(*) FROM `list` t1 WHERE t.Name = t1.Name) totle
FROM `list` t
) t1
where rn <> totle
sqlfiddle
[Results]:
| id | Name | Age | XXX | YYY |
|----|------|-----|-----|-----|
| 1 | aa | 12 | qqq | rr |
| 3 | bb | 13 | qhq | rr |
| 4 | bb | 13 | pqq | tr |

Related

Selecting COUNT and MAX columns with 2 tables and a bridge table

so what I am trying to do is having 3 tables (pictures, collections, and bridge) with the following columns:
Collections Table:
| id | name |
------------------
| 1 | coll1 |
| 2 | coll2 |
------------------
Pictures Table: (timestamps are unix timestamps)
| id | name | timestamp |
-------------------------
| 5 | Pic5 | 1 |
| 6 | Pic6 | 19 |
| 7 | Pic7 | 3 |
| 8 | Pic8 | 892 |
| 9 | Pic9 | 4 |
-------------------------
Bridge Table:
| id | collection | picture |
-----------------------------
| 1 | 1 | 5 |
| 2 | 1 | 6 |
| 3 | 1 | 7 |
| 4 | 1 | 8 |
| 5 | 2 | 5 |
| 6 | 2 | 9 |
| 7 | 2 | 7 |
-----------------------------
And the result should look like this:
| collection_name | picture_count | newest_picture |
----------------------------------------------------
| coll1 | 4 | 8 |
| coll2 | 3 | 9 |
----------------------------------------------------
newest_picture should always be the picture with the heighest timestamp in that collection and I also want to sort the result by it. picture_count is obviously the count of picture in that collection.
Can this be done in a single statement with table joins and if yes:
how can I do this the best way?
A simple method uses correlated subqueries:
select c.*,
(select count(*)
from bridge b
where b.collection = c.id
) as pic_count,
(select p.id
from bridge b join
pictures p
on b.picture = b.id
where b.collection = c.id
order by p.timestamp desc
limit 1
) as most_recent_picture
from collections c;
A more common approach would use window functions:
select c.id, c.name, count(bp.collection), bp.most_recent_picture
from collections c left join
(select b.*,
first_value(p.id) over (partition by b.collection order by p.timestamp desc) as most_recent_picture
from bridge b join
pictures p
on b.picture = p.id
) bp
on bp.collection = c.id
group by c.id, c.name, bp.most_recent_picture;

Having two MySQL tables, get the last result for each value of first table key

I have two tables:
TABLE_01
-------------------------------
| ID | Key1 | Key2 |
-------------------------------
| 1 | 504 | 101 |
| 2 | 504 | 102 |
| 3 | 505 | 101 |
| 4 | 505 | 103 |
| 5 | 508 | 101 |
| 6 | 510 | 104 |
| 7 | 509 | 101 |
-------------------------------
TABLE_02
----------------------------------------
| ID | T_01 | timestamp | data |
----------------------------------------
| 1 | 1 | ts_01 | ..abc.. |
| 2 | 1 | ts_02 | ..dfg.. |
| 3 | 2 | ts_03 | ..hij.. |
| 4 | 3 | ts_04 | ..klm.. |
| 5 | 1 | ts_05 | ..nop.. |
| 6 | 4 | ts_06 | ..qrs.. |
| 7 | 3 | ts_07 | ..tuv.. |
| 8 | 5 | ts_08 | ..wxy.. |
| 9 | 2 | ts_09 | ..z.... |
| 10 | 4 | ts_10 | ..abc.. |
----------------------------------------
On both table, ID is the Primary Incremental Key
In TABLE_01, the columns key1 + key2 are Unique Key (Can't be more than one Key1 Key2 couple)
In TABLE_02, the column T_01 makes reference on TABLE_01.ID
My goal is that given a key1 value, be able to get the last entry of TABLE_02 for each TABLE_01.ID with the correspondent timestamp on DESC ORDER.
For example, if I give 505, the output should be:
KEY1 | KEY2 | TIMESTAMP
---------------------------
505 | 103 | ts_10 ---> FROM TABLE_01.Id = 4
505 | 101 | ts_07 ---> FROM TABLE_01.Id = 3
As you can see, It only shows the last entry on the case of TABLE_01.ID = 4 (which is 505 | 103)
I have tried to do something like this:
SELECT `t1`.`Key1`, `t1`.`key2`, `t2`.`timestamp`
FROM `TABLE_02` AS t2
INNER JOIN `TABLE_01` AS t1
WHERE `t1`.`key1` = '505'
ORDER BY `t2`.`ID`
DESC LIMIT 100
The problem with this query is that since I am using t2.timestamp, I am receiving all the results instead of only ONE for EACH. Also, I'm not using correctly the TABLE_01.ID on TABLE_02.
If you just want the latest timestamp in the second table per combination of keys in the first table, you can join and aggregate:
select t1.key1, t1.key2, max(t2.timestamp) max_t2_timestamp
from table_01 t1
inner join table_02 t2 on t2.t_01 = t1.id
group by t1.key1, t1.key2
If you want the entire row of the second table, then I would recommend window functions:
select *
from (
select t1.key1, t1.key2, t2.*,
row_number() over(partition by t1.key1, t1.key2 order by t2.timestamp desc) rn
from table_01 t1
inner join table_02 t2 on t2.t_01 = t1.id
group by t1.key1, t1.key2
) t
where rn = 1

GROUP by version and display by row

+----------+--------+
| name | version|
+----------+--------+
| book | 2 |
| book | 1 |
| book | 1 |
| pen | 1 |
| pen | 2 |
| pen | 2 |
| pen | 2 |
| paper | 1 |
+----------+--------+
I have the table above and i want to make a query to group by name and count by version(row)
Result:
+----------+--------+--------+
| name | version| count |
+----------+--------+--------+
| book | 1 | 2 |
| book | 2 | 1 |
| pen | 1 | 1 |
| pen | 2 | 3 |
| paper | 1 | 1 |
| paper | 2 | 0 |
+----------+--------+--------+
The query would be
SELECT name, version, count(*) as count
FROM your_table_name
GROUP BY name, version
If you want all name/version combinations, then use a cross join to generate all rows and then left join to bring in the existing data:
select n.name, v.version, count(t.name)
from (select distinct name from t) n cross join
(select distinct version from t) v left join
t
on t.name = n.name and t.version = v.version
group by n.name, v.version
order by n.name, v.version;

Query previous row value and merge to current row

Is there a way how can I query the previous row value and merge to current row, here is my sample table scenario:
+-------------------------------------+
| ColA | ColB | ColValue | Date |
|------|------|----------|------------|
| AAA | 111 | 5 | 2017-04-23 |
| AAA | 111 | 4 | 2017-04-22 |
| AAA | 111 | 3 | 2017-04-21 |
| BBB | 222 | 5 | 2017-04-30 |
| BBB | 222 | 4 | 2017-04-29 |
+-------------------------------------+
And my expected result should be this, just want to get the previous and current value and group it by selected columns and date.
+--------------------------------------------------+
| ColA | ColB | PreValue | CurValue | Date |
|------|------|----------|-------------------------|
| AAA | 111 | 4 | 5 | 2017-04-23 |
| AAA | 111 | 3 | 4 | 2017-04-22 |
| AAA | 111 | N/A | 3 | 2017-04-21 |
| BBB | 222 | 4 | 5 | 2017-04-30 |
| BBB | 222 | N/A | 4 | 2017-04-29 |
+--------------------------------------------------+
any suggestions or solution, thanks in advance
Here is my actual query from my actual data as reference:
SELECT ai.APName, tbap.Value , tbap.DateTime, tbap.Comment, tbap.ModifiedBy, tbap.ToolName, d.Name as Strategy FROM (SELECT dt.*,ins.Value as ToolName FROM (SELECT av.*,ai.DocumentID,ai.IndexID FROM ControlAutomation.appartitionindexes ai
JOIN (SELECT * FROM ControlAutomation.appartitionvalues
where DateTime > '2017-04-22 23:17:13' and DateTime < '2017-04-26 23:18:28') av
ON ai.APPartitionID = av.APPartitionID) dt INNER JOIN factory.indexes ins ON ins.ID = dt.IndexID
where dt.comment like '%updateAdjustableParameter%'
group by dt.ID) tbap
INNER JOIN ControlAutomation.documents d ON d.ID = tbap.DocumentID
INNER JOIN appartitionindexes ai ON ai.APPartitionID = tbap.APPartitionID
GROUP BY tbap.ID
ORDER BY tbap.ToolName DESC, d.Name, tbap.DateTime DESC
LIMIT 100
Hope I correctly got your question: http://sqlfiddle.com/#!9/d815c/6
select
prev_query.cola as ColA,
prev_query.colb as ColB,
rhs.colvalue as PreValue,
prev_query.colvalue as CurValue,
prev_query.coldate as ColDate
from
prev_query left join prev_query as rhs
on prev_query.cola = rhs.cola
and prev_query.colb = rhs.colb
and prev_query.colvalue = rhs.colvalue + 1
order by
prev_query.cola, prev_query.colb, prev_query.coldate desc;

MYSQL sum from two different tables group by

I have two tables.
I need to combine each row of these two tables into a row in table3. I managed to get the table1 SUM amount but not table2.
Eg.
table user
+---------+-----------+
| user_id | user_name |
+---------+-----------+
| 001 | JOHN |
| 002 | ADAM |
+---------+-----------+
table1
+-----------+----------------+-------------------+---------------------+
| table1_id | table1_user_id | table1_amount | table1_date |
+-----------+----------------+-------------------+---------------------+
| 6 | 001 | 100 | 01/11/2014 10:55 |
| 7 | 002 | 100 | 01/11/2014 10:55 |
| 8 | 001 | 50 | 25/10/2014 10:55 |
| 9 | 001 | 100 | 23/10/2014 11:00 |
| 10 | 002 | 0 | 21/10/2014 11:00 |
+-----------+----------------+-------------------+---------------------+
table2
+-----------+----------------+----------------+--------------------+
| table2_id | table2_user_id | table2_amount | table2_date |
+-----------+----------------+----------------+--------------------+
| 1 | 001 | 100 | 15/11/2014 10:55 |
| 2 | 001 | 100 | 15/10/2014 10:55 |
| 3 | 002 | 100 | 11/10/2014 10:55 |
| 4 | 001 | 50 | 11/10/2014 10:55 |
+-----------+----------------+----------------+--------------------+
Expected Result:
Table3
+-----+---------+---------------+---------------+----------+---------+
| id | user_id | table1_amount | table2_amount | Year | Month |
+-----+---------+---------------+---------------+----------+---------+
| 1 | 001 | 100 | 100 | 2014 | 11 |
| 2 | 002 | 100 | 0 | 2014 | 11 |
| 3 | 001 | 150 | 150 | 2014 | 10 |
| 4 | 002 | 0 | 100 | 2014 | 10 |
+-----+---------+---------------+---------------+----------+---------+
My try but it does not show the expected result. The amount of table2_amount in every row is NULL :
SQL=" INSERT INTO table3
SELECT user_id,SUM(table1_amount),t2.amount2,
YEAR(table1_date),MONTH(table1_date) FROM table1 a
LEFT JOIN
(SELECT c.table2_user_id,SUM(c.table2_amount) as amount2,c.table2_date
FROM table2 c
GROUP BY DATE_FORMAT(c.table2_date,'%Y-%m'),c.table2_user_id ASC
) t2
on t2.table2_user_id = a.table1_user_id AND t2.table2_date = a.table1_date
GROUP BY DATE_FORMAT(a.table1_date,'%Y-%m'),table1_user_id ASC ";
"
This a nice task for UNION
SELECT tx.uid,SUM(tx.a1),SUM(tx.a2),YEAR(tx.d),MONTH(tx.d)
FROM
(
SELECT t1.table1_user_id as uid,
t1.table1_amount as a1,
0 as a2,
t1.table1_date as d
FROM table1 t1
UNION
SELECT t2.table2_user_id as uid,
0 as a1,
t2.table2_amount as a2,
t2.table2_date as d
FROM table2 t2
) tx
GROUP BY DATE_FORMAT(d,'%Y-%m'),uid ASC
Thanks to David162795 for the enlightening discussion.
The missed point is group INNER QUERY by date and user id when the date from two tables are different.
We need to group them by their individual date in the Inner Query and then group the main SELECT query by the time variable.
This goes my answer for this case :
$SQL = "
INSERT INTO table3 (user_id, table1_amount, table2_amount,Year, Month)
SELECT tx.uid, SUM(tx.sum1), SUM(tx.sum2),YEAR(tx.d) as year,MONTH(tx.d) as month
FROM
(SELECT b.table1_user_id as uid,b.table1_amount as sum1,0 as sum2,
b.table1_date as d FROM table1 b
GROUP BY DATE_FORMAT(d,'%Y-%m'),uid ASC
UNION
SELECT c.table2_user_id as uid,0 as sum1,
sum(c.table2_amount) as sum2,c.table2_date as d1
FROM table2 c
GROUP BY DATE_FORMAT(d1,'%Y-%m'),uid ASC
) tx
GROUP BY year,month,uid"