get unique records from mysql - mysql

Please check this query. Actually, I want to fetch only two records, not 4 records. I should get last record per from_member_id and to_member_id. Currently, I get two records for it. Please let me know what I am doing wrong in the query
Query:
SELECT DISTINCT from_member_id, to_member_id
FROM `single_chat`
where from_member_id = 175 or to_member_id = 175
group by from_member_id, to_member_id
Desired output
175 176
175 177

You can try the below - DEMO
SELECT distinct
t2.from_member_id,t2.to_member_id
FROM single_chat t1
JOIN single_chat t2 on t1.from_member_id=t2.to_member_id
and t1.to_member_id < t2.to_member_id
where t1.from_member_id = 175 or t1.to_member_id = 175
OUTPUT:
f_id t_id
175 176
175 177

Related

How to delete repeated data on MYSQL table to be able to apply an unique value constraint

I have a database with some fields and I want to apply a unique value constraint to a table:
ALTER TABLE assessment_submissions
ADD CONSTRAINT UC_Question UNIQUE (evaluated_user, evaluator_user, question_id);
But there is some data inside the table that doesn't allow me to put this constraint.
I got an error when I tried to apply the constraint:
SQL error 1062: Duplicate entry 154-154-45 for key UC_Question
Take a look at the image below:
The results on the line that starts with id 131271 and id 131413 have the same values on the fields evaluated_user, evaluator_user, and question_id.
This way it's not possible to apply the constraint.
I deleted the duplicated row, but I still was not able to apply the constraint.
I suppose there are more duplicate data inside that table. How can I find all data that is duplicated inside that table? Which query can I use to do that?
I have no idea where I can start.
Kindly use any of these two based on the need at hand. Essentially, you need to decide whether to retain the duplicate set that first hit the database or the set that arrived last.
DELETE t1 FROM assessment_submissions t1 INNER JOIN assessment_submissions t2
WHERE t1.id < t2.id
AND t1.evaluated_user=t2.evaluated_user AND t1.evaluator_user = t2.evaluator_user
AND t1.question_id=t2.question_id;
DELETE t1 FROM assessment_submissions t1 INNER JOIN assessment_submissions t2
WHERE t1.id > t2.id
AND t1.evaluated_user=t2.evaluated_user AND t1.evaluator_user = t2.evaluator_user
AND t1.question_id=t2.question_id;
I prefer the above approach for user environments that have MySQL versions earlier than 8.X
It depends a bit on whether you want to examine the rows before deciding which ones to delete or not (see 1. respectively 2. below).
Let's say you have the following example data:
id evaluated_user evaluator_user question_id answer_id
1 262 275 157 573
2 262 275 162 593
3 262 275 332 1260
4 262 275 161 589
5 262 275 157 573
6 262 275 157 1425
7 262 275 167 726
8 262 275 167 4593
If you want to take a look at the rows to get the information which ones you need to delete, just grouping by wont get you the ids.
If you have MySQL 8.0, you can use a window function to calculate the number of duplicates for each unique (evaluated_user, evaluator_user, question_id) combination as follows (ordering is optional):
select *, count(*) over (partition by evaluated_user, evaluator_user, question_id) as cnt
from example e
order by cnt desc, evaluated_user, evaluator_user, question_id
This will give you
id evaluated_user evaluator_user question_id answer_id cnt
1 262 275 157 573 3
5 262 275 157 573 3
6 262 275 157 1425 3
7 262 275 167 726 2
8 262 275 167 4593 2
4 262 275 161 589 1
2 262 275 162 593 1
3 262 275 332 1260 1
In this table, all entries with cnt > 1 are the rows you are interested in. If you just want them, wrap this into a select * from ... where cnt > 1.
For previous versions of MySQL (which don't support window functions), you can achieve the same using a query like the one from #DNNX's answer and joining the result with the original table:
select e.*
from example e
join (select evaluated_user, evaluator_user, question_id
from example
group by evaluated_user, evaluator_user, question_id
having count(*) > 1) f
on e.evaluated_user = f.evaluated_user and
e.evaluator_user = f.evaluator_user and
e.question_id = f.question_id
Either of this will give you the rows you may want to examine to decide which ones to delete:
id evaluated_user evaluator_user question_id answer_id
1 262 275 157 573
5 262 275 157 573
6 262 275 157 1425
7 262 275 167 726
8 262 275 167 4593
In case you don't need to inspect the data first to decide which records you delete, as long as one line remains, you can build your DELETE statement elegantly using the RANK() function (again, MySQL 8.0):
with subtab as (select id, rank() over (partition by evaluated_user, evaluator_user, question_id order by id) as rnk
from example)
delete from example e
where e.id in (select id
from subtab
where rnk > 1)
This example will bulk delete all duplicate rows except the one with the smallest id for each unique combination. You can modify the order by statement to influence which records to delete. For example, to keep the record with the highest id instead, you can order by id desc. Or if you wanted to keep the one with the smallest answer_id, you order by answer_id. Note: if you have duplicate entries in the column you are ordering by, you may end up with more than one row left. To avoid that, use row_number() instead of rank().
To get the same without window functions, you can use
delete e from example e
join example f
on e.evaluated_user = f.evaluated_user
and e.evaluator_user = f.evaluator_user
and e.question_id = f.question_id
and e.id > f.id ;
Again, the statement can be modified depending on which row you want to keep. For example, to keep the one with the highest answer_id, you change the last condition to and e.answer_id < f.answer_id.
See this db<>fiddle for all of the above in action.
This query will return all duplicate rows, along with a couple of IDs belonging to the same "group".
select evaluated_user, evaluator_user, question_id, count(*), min(id), max(id)
from assessment_submissions
group by evaluated_user, evaluator_user, question_id
having count(*) > 1

Need to solve a MYSQL select query

SELECT
points.location_id,
route_locations.route_id
FROM
points
LEFT JOIN route_locations ON route_locations.location_id = points.location_id
WHERE
points.id = 199 OR points.id = 205
after this query I am getting this result..
route_id location_id
12 69
12 75
14 75
now I need the common value for location_id 69 and location_id 75.. ( here route_id 12)
How can I get that by Query..
You can try below query -
SELECT
route_locations.route_id
FROM points
JOIN route_locations ON route_locations.location_id = points.location_id
WHERE
points.id in (199,205)
group by route_locations.route_id
having count(points.location_id)=2

Select corresponding value from second table (Mysql)

Struggling with some sql, would appreciate some guidance.
Have two tables logs and sense
logs –
assetid ts nodeid status
1 2017-10-26 14:00:10 73 240
2 2017-10-26 14:00:06 21 160
3 2017-10-26 14:00:04 18 230
4 2017-10-26 14:00:02 19 400
5 2017-10-26 14:00:00 21 190
1 2017-10-26 13:20:08 18 20
2 2017-10-26 13:06:10 20 160
3 2017-10-26 13:03:04 17 230
sense –
status value
20 5
160 37
190 39
230 56
240 58
400 90
Trying to find the correct syntax to only show the latest record (in datetime) of each assetid and then show the corresponding value from the sense table (based on the matching status in both tables) to produce –
assetid ts nodeid status value
1 2017-10-26 14:00:10 73 240 58
2 2017-10-26 14:00:06 21 160 37
3 2017-10-26 14:00:04 18 230 56
4 2017-10-26 14:00:02 19 400 90
5 2017-10-26 14:00:00 21 190 39
Have tried –
Select assetid, ts, nodeid, status, value
From
logs
Join sense X on X.status = logs.status
Group by assetid
Order by ts DESC
But this only outputs 1 row (instead of 5)
assetid ts nodeid status value
1 2017-10-26 14:00:10 73 240 58
Removing
Join sense X on X.status = logs.status
of course outputs all records but that is not required.
Thoughts appreciated.
Regards
Active
Actually your query is returning 5 rows, 1 for each id. But it won't return rows with latest ts for each id. You can verify this by clicking on the link for demo. You can compare results of both queries.
To achieve this task,following query will help you:
Select l.assetid, l.ts, logs.nodeid, X.status, X.value
From
logs
inner Join sense X on X.status = logs.status
inner join (select assetid, max(ts) as ts from logs group by assetid) l
on l.assetid = logs.assetid and logs.ts = l.ts
Group by l.assetid
Order by l.ts DESC;
Click here for Demo
EDIT:
If dataype of ts is string then replace max(ts) in above query with:
max(str_to_date(ts,'%d%m%y'))
Feel free to ask any doubts.
Hope it helps!
Try this
Select a1.assetid, MAX(a1.ts), a1.nodeid, a1.status, X.value
From
logs a1
inner join sense X on X.status = a1.status
Group by assetid, a1.nodeid, a1.status, X.value
Order by ts DESC
Use GROUP BY to find minimum for each assetid and then JOIN with the logs and sense
Select *
FROM logs l
JOIN sense s ON s.status = l.status
JOIN
(
Select assetid, max(ts) maxts
From logs
Group by assetid
) t ON t.assetid = l.assetid and l.ts = t.maxts
demo
On MY SQL 8.0.2
WITH CTE as
(
Select A.assetid, A.ts, A.nodeid, A.status, B.value, row_number() over(PARTITION BY A.assetid ORDER BY A.ts DESC) AS rn
from logs as A
inner join sense B ON A.status=B.status
)
SELECT *
FROM CTE
WHERE rn='1';

Join two tables using mysql

table:tab1
id date_time zoneid accountid slotid trequest bidder width height
_50832 2017-09-04 15:41:06 153 1654 153x468x60 10 aaa 468 60
_50832 2017-09-04 15:41:06 152 1654 152x468x60 10 bbb 468 60
table:tab2
id date_time zoneid accountid slotid bidder count
_50832 2017-09-04 15:41:06 152 1654 152x468x60 bbb 6
_50832 2017-09-04 15:41:06 152 1654 152x468x60 bbb 4
_50832 2017-09-04 15:41:06 153 1654 153x468x60 aaa 9
_50832 2017-09-04 15:41:06 153 1654 153x468x60 aaa 1
below is my query:
SELECT SUM(req.trequest) as REQ, SUM(win.count) as IMP
FROM tab1 as req
JOIN tab2 as win ON (req.id=win.id AND req.zoneid=win.zoneid)
GROUP BY req.zoneid
I get below result,
REQ IMP
20 10
20 10
IMP count is correct but I get wrong REQ count. My expected result is
REQ IMP
10 10
10 10
How to get my expected result?
Lets find the sum of trequest and count separately based on zoneid and id.Then use these two results ( t1 and t2 ) in the inner join.
Count mismatch problem shown in the question occur due to multiple rows satisfying the joining conditions.
In this solution we will only have one entry for each zoneid in both the results ( t1 and t2 ). So the problem is avoided.
Note: You can remove the id column from the GROUP BY clause if it doesn't make any difference.
SELECT t1.id, t1.zoneid, t1.REQ, t2.IMP FROM
(SELECT id,zoneid,SUM(trequest) as REQ
FROM tab1 GROUP BY zoneid,id ) t1
INNER JOIN
(SELECT id,zoneid SUM(win.count) as IMP
FROM tab2 GROUP BY zoneid,id ) t2
ON t1.id = t2.id
AND t1.zoneid = t2.zoneid
Let's try first sumwin.count and group records in sub-query, after it join tables. Try in following:
SELECT SUM(req.trequest) as REQ, SUM(win.count) as IMP
FROM tab1 as req
JOIN (
SELECT SUM(win.count) as IMP, win.zoneid, win.id
FROM tab2 as win
GROUP BY win.zoneid, win.id) AS win ON req.id=win.id AND req.zoneid=win.zoneid
GROUP BY req.zoneid
Instead of req.zoneid. You should try win.zoneid. What seems is that the rows in table 1 are counted multiple times as zoneid in table 2 comes twice. So win.zoneid would group it and avoid the repetition.
Updated: The solution posted by #mayur panchal is the correct one as you don't need to SUM the rows in first table as they belong to different zoneid. If you SUM them you will obviously get the 20 repeated twice.

Have to get the corresponding time stamp when i get max of a column from a table

I need to extract the required fields from a table along with relevant time stamp
SELECT * FROM Glm_Test.LicenseUsage where FeatureId='2';
Output :
VendorId,FeatureId,Total_Lic_Installed,Total_Lic_Used,Reserved,CurrentTime
1 2 106 19 67 2015-12-15 15:00:05
1 2 106 19 67 2015-12-15 15:02:02
1 2 106 19 69 2015-12-15 15:04:02
1 2 106 19 67 2015-12-15 15:06:01
1 2 106 20 67 2015-12-15 15:08:02
select VendorId,FeatureId,Total_Lic_Installed,Max(Total_Lic_Used),Reserved,CurrentTime from Glm_Test.LicenseUsage where FeatureId= '2' group by VendorId,FeatureId;
output:
1 2 106 20 69 2015-12-15 15:00:05
In the above 2 queries
1st query lists all entries from the table
and i want second query to return time stamp for the MAX value of column Total_Lic_Used but somehow it is returning me only timestamp of the first entry.
Help is much appreciated.
Selecting the columns which are not part of an aggregation function like count/max/min/sum... or not in group by clause will give unexpected results:
Other RBBMS wont allow these statements(gives error like):
sql server ==> the select list because it is not contained in either
an aggregate function or the GROUP BY clause
Oracle ==>not a GROUP BY expression
You can do this by a sub query and join
select
a.VendorId,
a.FeatureId,
a.Total_Lic_Installed,
b.max_Total_Lic_Used,
a.Reserved,
a.CurrentTime
from Glm_Test.LicenseUsage a
join (
select
VendorId,
FeatureId,
Max(Total_Lic_Used) max_Total_Lic_Used
from Glm_Test.LicenseUsage
where FeatureId = '2'
group by VendorId, FeatureId
) b
on a.VendorId = b.VendorId and
a.FeatureId = b.FeatureId and
a.Total_Lic_Used = b.max_Total_Lic_Used
sql fiddle demo
You can try this also;
select
`VendorId`,
`FeatureId`,
`Total_Lic_Installed`,
`Total_Lic_Used`,
`Reserved`,
`CurrentTime`
from Glm_Test.LicenseUsage
order by Total_Lic_Used desc
limit 1
demo