I'm trying to generate a query to tell me how many products were ordered from the site, but in groups.
This is the structure of my table (product_orders):
product_order_id | order_id | product_id
168 | 64 | 17
168 | 64 | 18
168 | 64 | 16
168 | 64 | 15
168 | 64 | 19
168 | 65 | 17
168 | 65 | 18
168 | 66 | 16
168 | 66 | 15
168 | 66 | 19
168 | 67 | 15
What I need to be able to get, is a count of orders where the user purchased:
ONLY product_id 17 AND 18
ONLY product_id 17 AND 16 AND 15
It's that AND that's driving me a bit crazy with this query, and the fact that that 1 order has multiple products. Any ideas? I'm sure I'm overlooking something simple here.
Thanks.
B.
You can do this with a fairly clunky EXISTS statement
SELECT COUNT(DISTINCT order_id) FROM product_orders p1
WHERE EXISTS (SELECT * FROM product_orders p2
WHERE p1.order_id = p2.order_id
AND p2.product_id = 17)
AND EXISTS (SELECT * FROM product_orders p3
WHERE p1.order_id = p3.order_id
AND p3.product_id = 18)
AND NOT EXISTS (SELECT * FROM product_orders p4
WHERE p1.order_id = p4.order_id
AND p4.product_id <> 17
AND p4.product_id <> 18);
And you can obviously you repeat this pattern for the {15,16,17} set.
Assuming the product_id's are unique per order in product_orders table, we can count matching and non-matching and compare. So there should be exactly two entries with product_id 17 or 18, and none that are not 17 or 18 to match the first scenario. The second scenario would be the same logic, except there should be exactly three entries with product_id 15 or 16 or 17, and none that don't match any:
select count(*) from (
select distinct order_id from product_orders po1
where (
(select count(product_id) from product_orders po2
where po1.order_id = po2.order_id and
po2.product_id in (17, 18)) = 2
and
(select count(product_id) from product_orders po3
where po1.order_id = po3.order_id and
po3.product_id not in (17, 18)) = 0
) or (
(select count(product_id) from product_orders po4
where po1.order_id = po4.order_id and
po4.product_id in (15, 16, 17)) = 3
and
(select count(product_id) from product_orders po5
where po1.order_id = po5.order_id and
po5.product_id not in (15, 16, 17)) = 0
)
) p
There is only one order that satisfies all the conditions: order_id 65.
Working Demo: http://sqlize.com/5Q5Lo7Oa71
Related
I am trying to get the average ratings of a user by project type but include all users that have ratings regardless of type.
SELECT projects.user_id, AVG(ratings.rating) AS avg1
FROM projects
JOIN ratings
ON projects.project_id = ratings.project_id
WHERE projects.type='0'
GROUP BY projects.user_id;
Thanks in advance for the help.
The output I get for type 0 is:
user_id | avg1
-----------------
11 | 2.25
but I am trying to get:
user_id | avg1
-----------------
11 | 2.25
12 | 0
because user 12 has a project in the rating table but not of type 0 I still want it output with avg1 = 0
The output for type 1 works as expected because all users that have ratings also have type 1:
user_id | avg1
-----------------
11 | 4
12 | 2.5
Projects table is: (only the first 4 projects are in the ratings table)
project_id |user_id | type
--------------------------
51 11 0
52 12 1
53 11 0
54 11 1
55 12 1
56 13 0
57 14 1
Ratings table is:
project_id | rating
-------------------
51 0
51 1
52 4
51 5
52 2
53 3
54 4
52 1.5
Use conditional aggregation:
SELECT p.user_id,
COALESCE(AVG(CASE WHEN p.type = '0' THEN r.rating END), 0) AS avg1
FROM projects p JOIN ratings r
ON p.project_id = r.project_id
GROUP BY p.user_id;
See the demo.
I have a following result set:
request_id | p_id
66 | 10
66 | 10
66 | 10
66 | 22
66 | 22
76 | 23
76 | 24
I am trying to select rows that excludes records with certain combination values:
request_id | product_id
66 | 10
76 | 23
So the output result set should contain only these records:
66 | 22
66 | 22
76 | 24
I tried doing:
select * from `table`
where request_id NOT IN (66, 76) AND product_id NOT IN (10, 22)
But this gives me empty resultset.
How do I exclude just the combination of those two values?
You can try below -
DEMO
select * from `table`
where (request_id, p_id) NOT IN ((66, 10),(76,23))
OUTPUT:
request_id p_id
66 22
66 22
76 24
Try use something like this:
SELECT DISTINCT *
FROM TABLE1
WHERE TABLE1.request_id NOT IN
(
SELECT r_id
FROM TABLE2
)
AND TABLE1.p_id NOT IN
(
SELECT p_id
FROM TABLE2
)
That is a better way:
SELECT DISTINCT *
FROM TABLE1
LEFT JOIN TABLE2 ON TABLE1.request_id = TABLE2.r_id
AND TABLE1.p_id = TABLE2.p_id
WHERE TABLE2.p_id IS NULL
I have a MySQL table of customers and the shop branches they have purchased from, similar to the following:
customer_id | branch_id | is_major_branch
-----------------------------------------------
5 24 1
5 83 0
5 241 0
8 66 0
8 72 0
9 15 1
16 31 1
16 61 1
is_major_branch is 1 if that branch is a particularly large store.
How can I delete all rows where a customer has shopped in a minor branch (is_major_branch = 0), except if a customer has only ever shopped in a minor branch? Example result set:
customer_id | branch_id | is_major_branch
-----------------------------------------------
5 241 1
8 66 0
8 72 0
9 15 1
16 31 1
16 61 1
Notice how customer 8 has only ever shopped in a minor branches, so we ignore them from the deletion.
You can delete the rows doing:
delete t
from t join
(select customer_id, max(is_major_branch) as max_is_major_branch
from t
group by customer_id
) tt
on t.customer_id = tt.customer_id
where t.is_major_branch = 0 and tt.max_is_major_branch = 1;
If you just want a select query, then use exists:
select t.*
from t
where not (t.is_major_branch = 0 and
exists (select 1 from t t2 where t2.customer_id = t.customer_id and t2.is_major_branch = 1)
);
I have been trying to get the current winning streak based on a results table. I am having lots of trouble with this though. I have managed to get what the teams highest win streak for each team over the full period is. But what I cannot manage to do is get the current win streak if they have one based on the last result per team.
For example the below table where the result is 'H' indicated as win.
TeamID Result Date
25 A 02/12/17
25 H 16/12/17
25 D 22/12/17
25 D 03/01/18
25 H 20/01/18
28 D 09/12/17
28 D 23/12/17
28 H 01/01/18
28 H 20/01/18
58 H 02/12/17
58 A 16/12/17
58 H 23/12/17
58 H 01/01/18
58 D 20/01/18
61 D 03/12/17
61 A 17/12/17
61 D 26/12/17
61 H 30/12/17
61 H 14/01/18
So TeamID 25 has a current win streak of 1. TeamID 28 a win streak of 2 and Team 58 a win streak of 0 as there last result was 'D' (Draw)
I have used the below code which I can get working but not with the additional grouping the results come back all as 1 which is clearly wrong.
http://www.sqlteam.com/article/detecting-runs-or-streaks-in-your-data
I have looked at other bits of code on here but none of them appear to be based on the last result or they are SQL Server based but I need the code to work in MySQL. Thanks for any help on this.
How to ask a question;
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(TeamID INT NOT NULL
,Result CHAR(1) NOT NULL
,Date DATE NOT NULL
,PRIMARY KEY(teamid,date)
);
INSERT INTO my_table VALUES
(25,'A','2017-12-02'),
(25,'H','2017-12-16'),
(25,'D','2017-12-22'),
(25,'D','2018-01-03'),
(25,'H','2018-01-20'),
(28,'D','2017-12-09'),
(28,'D','2017-12-23'),
(28,'H','2018-01-01'),
(28,'H','2018-01-20'),
(58,'H','2017-12-02'),
(58,'A','2017-12-16'),
(58,'H','2017-12-23'),
(58,'H','2018-01-01'),
(58,'D','2018-01-20'),
(61,'D','2017-12-03'),
(61,'A','2017-12-17'),
(61,'D','2017-12-26'),
(61,'H','2017-12-30'),
(61,'H','2018-01-14');
How to answer one:
SELECT a.*
FROM
( SELECT x.*
, CASE WHEN #prev=teamid THEN CASE WHEN result = 'H' THEN #i:=#i+1 ELSE #i:=0 END ELSE #i:=0 END i
, #prev:=teamid prev
FROM my_table x
, (SELECT #prev:=null,#i:=0) vars
ORDER
BY teamid
, date
) a
JOIN
( SELECT teamid
, MAX(date) date
FROM my_table
GROUP
BY teamid
) b
ON b.teamid = a.teamid
AND b.date = a.date;
+--------+--------+------------+------+------+
| TeamID | Result | Date | i | prev |
+--------+--------+------------+------+------+
| 25 | H | 2018-01-20 | 1 | 25 |
| 28 | H | 2018-01-20 | 2 | 28 |
| 58 | D | 2018-01-20 | 0 | 58 |
| 61 | H | 2018-01-14 | 2 | 61 |
+--------+--------+------------+------+------+
I am using MySQL. I'm trying to build something and just can't find a solution to a problem.
I am selecting a value from the lookup table based on my table as shown in the below example.
Select Criteria:
my.id<>l.id AND my.route1=l.route1 AND my.route2=l.route2 AND my.utc=l.utc
where my.stime is closest or same as l.stime
ex) my.id=2's col should get the l.id=1, l.etime=7777 since my.id<>l.id and the rest are the same.
ex) my,id=5's col has options l.id=3, l.etime=9999 and l.id=4, l.etime=7979 since my.id<>l.id, my.route=l.route, my.utc=l.utc. Yet, since my.stime=2220 is closer to l.stime=2222 than l.stime=3333 , l.id=3, l.etime=9999 will be chosen.
ex) my,id=6's col example is to select either value if "closest" is the same.
ex) my,id=7's col example is to return NULL when the criteria is not met.
Table: lookup (l.)
id route1 route2 utc stime etime
---|--------|--------|-----|-------|------
1 11 22 111 1111 7777
2 11 22 111 1111 8888
3 22 33 222 2222 9999
4 22 33 222 3333 7979
5 22 33 222 3335 8989
Table: my (my.) | result
id route1 route2 utc stime | l.id l.etime
---|--------|--------|-----|------- |-------|----------|
2 11 22 111 1111 | 1 7777
5 22 33 222 2220 | 3 9999
6 22 33 222 3334 | 4or5 7979or8989
7 22 33 999 9999 | null null
A new table should be created where the result is appended to the last col of my.
Any help is appreciated. Thanks in advance.
This solution is a bit convoluted, but it's a starting point.
First, let's create an auxiliary table:
CREATE TEMP TABLE temp AS
SELECT m.id mid, l.id lid, ABS(l.stime-m.stime) timediff
FROM my m JOIN lookup l
WHERE m.route1 = l.route1 AND m.route2 = l.route2 AND
m.utc = l.utc AND m.id <> l.id;
From this table we can get the minimum timediff for each my.id:
SELECT mid, min(timediff) mtimediff FROM temp GROUP BY mid
Result:
mid mtimediff
---------- ----------
2 0
5 2
6 1
Now we can find which rows in lookup have this stime difference, and choose the smallest id:
SELECT t.mid mid, min(lid) lid
FROM temp t JOIN (
SELECT mid, min(timediff) mtimediff FROM temp GROUP BY mid
) mt ON t.mid = mt.mid AND t.timediff = mt.mtimediff
GROUP BY t.mid
This is the result:
mid lid
---------- ----------
2 1
5 3
6 4
And finally we use those ids to extract the data from the tables:
SELECT m.id, m.route1, m.route2, m.utc, m.stime, l.id, l.etime
FROM my m JOIN lookup l JOIN (
SELECT t.mid mid, min(lid) lid
FROM temp t JOIN (
SELECT mid, min(timediff) mtimediff FROM temp GROUP BY mid
) mt ON t.mid = mt.mid AND t.timediff = mt.mtimediff
GROUP BY t.mid
) ON m.id = mid AND l.id = lid;
Giving:
id route1 route2 utc stime id etime
---------- ---------- ---------- ---------- ---------- ---------- ----------
2 11 22 111 1111 1 7777
5 22 33 222 2220 3 9999
6 22 33 222 3334 4 7979