MySQL, How do I only select rows based on other rows data? - mysql

Ok, here's the deal, I have a table for stats where there are different types and I only want to select a row if it has a previous (not directly previous) row that has some matching data (hash) and is unique based on another column value.
In the following example we need to only get SALE rows
This example should help:
id link_id member_id stat_type hash
----------------------------------------------
108 41 82 SALE fffff
107 41 82 CLICK fffff
106 41 82 CLICK eeeee
105 41 67 SALE ddddd
104 41 67 CLICK ddddd
103 41 35 SALE ccccc
102 41 35 CLICK bbbbb
101 41 35 CLICK aaaaa
The only row I want to get back here is member_id = 67 because that member's only previous CLICK to link_id 41 has the same hash. Members 82 and 35 do not get selected because they both have a previous click to link_id 41 with a mismatched hash.
Query result should be:
id link_id member_id stat_type hash
----------------------------------------------
105 41 67 SALE ddddd
TIA

If I've understood your problem correctly, then you first need to group the table by link_id and member_id, filter those groups for ones that contain only one distinct hash, and then join the results back to your table to obtain all the matching SALE records:
SELECT * FROM my_table NATURAL JOIN (
SELECT link_id, member_id
FROM my_table
GROUP BY link_id, member_id
HAVING COUNT(DISTINCT hash) = 1
) t WHERE stat_type = 'SALE'
See it on sqlfiddle.

One way to get this result is to use an anti-join pattern.
SELECT r.*
FROM mytable r
LEFT
JOIN ( SELECT t.member_id
, t.link_id
, t.hash
FROM mytable t
GROUP
BY t.member_id
, t.link_id
, t.hash
) s
ON s.member_id = r.member_id
AND s.link_id = r.link_id
AND NOT (s.hash = r.hash)
WHERE r.stat_type = 'SALE'
AND s.member_id IS NULL
The inline view (derived table) aliased as s gets all of the distinct hash values for each member_id and link_id. That is left joined to all of the rows in the table that have stat_type='SALE', with matching member_id and link_id, and with a NON-matching hash.
The "trick" is the s.member_id IS NULL predicate. Any rows that found a match (i.e. a non-matching HASH will have a non-null value for s.member_id. Only rows that did not have a match will return a NULL (due to the LEFT JOIN).

Related

Ok to use select in where query?

I have 2 tables that contains product sets IDs containing product IDs. Nid is product set ID and second row contains products attached to the set. The reason why 2 tables is that first table contains paid products, second table contains free products. Based on existing products in cart, I created a query to get results like this:
1 product in cart having ID = 45
result:
nid
field_prod_in_set_nid
field_additional_set_prod_nid
67
45,45
45
query: ($items_string is variable containing product IDs)
SELECT i.nid, GROUP_CONCAT(i.field_prod_in_set_nid SEPARATOR ',') as set_products, ad.additional
FROM {content_field_prod_in_set} i
JOIN (
SELECT c.nid, GROUP_CONCAT(c.field_additional_set_prod_nid SEPARATOR ',') as additional
FROM
{content_field_additional_set_prod} c
GROUP BY c.nid) ad ON ad.nid = i.nid
WHERE
i.nid IN (SELECT nid FROM {content_field_prod_in_set} WHERE field_prod_in_set_nid IN ($items_string))
GROUP BY i.nid
content_field_prod_in_set
nid
field_prod_in_set_nid
62
3
62
3
64
3
63
15
64
25
62
29
67
45
67
45
content_field_additional_set_prod
nid
field_additional_set_prod_nid
62
46
62
9
63
NULL
64
46
67
45
QUESTION :
Is it possible to write the query without having select in WHERE ? I read that this is not a good practice and it is slow. Thank you.
Join with the subquery instead of using WHERE IN.
SELECT i.nid, GROUP_CONCAT(i.field_prod_in_set_nid SEPARATOR ',') as set_products, ad.additional
FROM content_field_prod_in_set i
JOIN (
SELECT c.nid, GROUP_CONCAT(c.field_additional_set_prod_nid SEPARATOR ',') as additional
FROM
content_field_additional_set_prod c
GROUP BY c.nid) ad ON ad.nid = i.nid
JOIN (SELECT DISTINCT nid
FROM content_field_prod_in_set
WHERE field_prod_in_set_nid IN ($items_string)
) i2 ON i2.nid = i.nid
GROUP BY i.nid
DEMO
SELECT DISTINCT is needed in the subquery to avoid duplicating the results in the GROUP_CONCAT() for each matching nid.

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

mysql count(*) in joins with one-to-many table

I need to perform a COUNT on a quite a big query, where one of the joined tables has a one-to-many relationship. This is throwing off my result as all data is being multiplied by the number of times an item is repeated in the 'many' side of the one-to-many table.
This is a shortened version of the query showing only the relevant portion to highlight the issue:
SELECT COUNT(trimtype) FROM versiontrim
INNER JOIN trims USING (trim_id)
INNER JOIN prices USING(version_id)
INNER JOIN m_versions USING(version_id)
WHERE trimtype IN('sec', 'help') AND price BETWEEN 200001 AND 210000
GROUP BY version_id
All tables are quite straighforward except m_versions that has the one-to-many relationship and looks like this:
version_id serv_id
1 1
1 2
1 3
1 4
1 5
.... and so on
The expected result of the query is :
version_id COUNT(trimtype)
44 9
54 7
69 9
214 10
216 6
282 1
290 10
Instead I am getting this,ie, all counts multiplied by 5 which is the number of times version_id is repeated in the m_versions table:
version_id COUNT(trimtype)
44 45
54 35
69 45
214 50
216 30
282 5
290 50
How to avoid this behavior?
Thanks
It matches to multiple records on table m_version that is why you are getting invalid result. Try wrapping it a subquery,
INNER JOIN (SELECT DISTINCT version_id FROM m_versions) m USING(version_id)
UPDATE
So the full query will look like this,
SELECT version_id, COUNT(trimtype)
FROM versiontrim
INNER JOIN trims USING (trim_id)
INNER JOIN prices USING(version_id)
INNER JOIN (SELECT DISTINCT version_id FROM m_versions) m USING(version_id)
WHERE trimtype IN('sec', 'help') AND price BETWEEN 200001 AND 210000
GROUP BY version_id

get records which are not present within same table against one criteria

I want to get IDs of users which are subscribed to one post but in some other type.
Please check the table below
Table: subscribers
userid post_id notification_made
33 780 2
35 780 2
29 780 2
33 780 1
35 780 1
Here in above image, While inserting the last 2 records I used insert query to add 33 and 35 with notification_made = 1. Here I want to get the IDs of users who are in this table with same post_id (780) But those are not under same notification_id (1). So I need 29 Userid which is under same post_id but not under same notification_made.
Please help !
Thanks in advance.
Considering you have three parameters (two userids and a notification mode), and you want to insert, you can try:
INSERT INTO subscribers
SELECT DISTINCT a.userid, a.post_id, 1
FROM subscribers a
INNER JOIN
(
SELECT post_id
FROM subscribers
WHERE userid IN (33,35) AND notification_made = 1
GROUP BY post_id
HAVING COUNT(*) = 2
) b ON a.post_id = b.post_id
WHERE a.userid NOT IN (33,35) AND a.notification_made <> 1