How to count with two "HAVING" situation in sql? - mysql

I need to count how many id_no does have ALL line_number with test_activity = approved and test_status = completed and filtered by work_type which is B.
id id _no line_number work_type date test_activity test_status
P P-01 1 b 2020-02-02 approved completed
P P-01 2 b 2020-02-02 approved completed
P P-01 3 b 2020-02-02 approved completed
P P-02 4 b 2020-02-02 approved incompleted
P P-02 5 b 2020-02-02 approved incompleted
P P-02 6 b 2020-02-02 approved completed
Q Q-01 7 b 2020-02-02 in progress incompleted
Q Q-01 8 b 2020-02-02 in progress incompleted
Q Q-01 9 b 2020-02-02 not started incompleted
Q Q-02 10 b 2020-02-02 approved completed
Q Q-02 11 b 2020-02-02 approved completed
Q Q-02 12 b 2020-02-02 approved completed
I tried:
select date, count(*) as count
from (select id, id_no, date
from pt_pretest pp where work_type = 'b'
group by id, id_no, date
having min(test_activity) = max(test_activity) and min(test_activity ) = 'approved'
and having min(test_status) = max(test_status) and min(test_status) = 'completed'
) as x group by date
But it did not work. From the sample above, the expected result is:
id date count
P 2020-02-02 1
Q 2020-02-02 1
How should I write my query so I will the expected result?
Thankyou

There is one having too many in your query. Remove it.
select id, id_no, date, count(*)
from pt_pretest pp
where work_type = 'b'
group by id, id_no, date
having min(test_activity) = max(test_activity) and min(test_activity ) = 'approved'
and min(test_status) = max(test_status) and min(test_status) = 'completed'
order by id, id_no, date;

The following returns all ids that have at least one id_no that fails the condition:
select id, count(*)
from (select id, id_no
from t
where not (test_activity = 'approved' and test_status = 'completed')
group by id, id_no
) ii
group by id;
If you want zeros in the result set, use conditional aggregation in the subquery:
select id, count( num_bad > 0 )
from (select id, id_no,
sum( not (test_activity = 'approved' and test_status = 'completed') ) as num_bad
from t
group by id, id_no
) ii
group by id;

Related

Get the row with latest date after inner join

I have this query:
SELECT
achievements.id,
achievements.name,
achievements.category,
userAchievements.createdAt
FROM
achievements
INNER JOIN
userAchievements ON userAchievements.achievementId = achievements.id
AND userAchievements.userId = 12
WHERE
achievements.type = 2
Result of this query is:
id
name
category
createdAt
8
First
1
2021-02-11
13
Second
2
2021-02-12
14
Third
4
2021-03-01
15
Fourth
4
2021-03-02
I have to leave only unique category with max createdAt field.
I need a query that will give the following result:
id
name
category
createdAt
8
First
1
2021-02-11
13
Second
2
2021-02-12
15
Fourth
4
2021-03-02
If I use group by then it returns row with id 14.
MySQL version - 5.7.33.
Achievements table:
id
name
category
8
First
1
13
Second
2
14
Third
4
15
Fourth
4
UserAchievements table:
id
achievementId
userId
createdAt
3
8
12
2021-02-11
7
13
12
2021-02-12
36
15
12
2021-03-02
40
14
12
2021-03-01
P.S.
I managed to write a query that solves the problem
SELECT
*
FROM
(SELECT
*,
IF(#prev <> category, #rn:=0, #rn),
#prev:=category,
#rn:=#rn + 1 AS rn
FROM
(SELECT
achievements.id,
achievements.name,
achievements.category,
userAchievements.createdAt
FROM
achievements
INNER JOIN userAchievements ON userAchievements.achievementId = achievements.id
AND userAchievements.userId = 12
WHERE
achievements.type = 2) as ach, (SELECT #rn:=0) rn, (SELECT #prev:='') prev
ORDER BY ach.createdAt DESC) t
WHERE
rn = 1
You can use window functions:
SELECT a.*
FROM (SELECT a.*,
ROW_NUMBER() OVER (PARTITION BY a.category ORDER BY a.created_at DESC) as seqnum
FROM achievements JOIN
userAchievements ua
ON ua.achievementId = a.id AND
ua.userId = 12
WHERE a.type = 2
) a
WHERE seqnum = 1;
This subquery basically search for all maximum dates, grouped by category.
SELECT MAX(_ua.createdAt) FROM `achievements` _a INNER JOIN user_achievements _ua ON _a.id = _ua.achievementId group by _a.category
So you maybe you can do :
SELECT a.* FROM achievements a
INNER JOIN user_achievements ua ON a.id = ua.achievementId
AND ua.createdAt IN (SELECT MAX(_ua.createdAt) FROM `achievements` _a INNER JOIN
user_achievements _ua ON _a.id = _ua.achievementId group by _a.category)

MYSQL Query for the below

I have join query which is not returning the result as expected. Below is the table structure, query used, result and expected result
Table A:
Id Name Token
1 A abcdef
2 B think
3. C Bxjscmsdnj
Table B:
id TableA_id configKey configValue
1 2 pmt ins
2 2 vat gas
3 1 vat nnnb
4 1 pmt mc
5 3 vat nhu
6 3 pmt nnu
7 2 hit bxhsjab
Below is the query that I used:
SELECT A.Token,
A.Name,
CASE
WHEN B.configKey = 'pmt’ THEN B.configValue
ELSE ''
END AS ‘PMT’,
CASE
WHEN B.configKey = ‘vat’ THEN B.configValue
ELSE ''
END AS ‘VAT’
FROM TABLEA A
INNER JOIN TABLEB B ON A.Id = B.TableA_id
WHERE B.configKey IN (‘PMT’, ‘VAT’)
ORDER BY A.id DESC;
Result:
Token Name PMT VAT
1 A nnnb
1 A mc
2 B gas
2 B ins
Expected Result:
Token Name PMT VAT
1 A mc nnnb
2 B ins gas
You want conditional aggregation. First, add a group by clause that groups together rows having the same id and name; then, wrap the case expressions in an aggregate function such as max():
select
a.id,
a.name,
max(case when b.configkey = 'pmt' then b.configvalue end) pmt,
max(case when b.configkey = 'vat' then b.configvalue end) vat
from tablea a
inner join tableb b on a.id = b.tablea_id
where b.configkey in ('pmt', 'vat')
group by a.id, a.name
order by a.id desc;

How can I retrieve Similar Orders In Mysql?

i need a query that should first look the oldest order which has status 0 (zero). and retrieves all the similar orders of that kind(matches exact total qty, itemSku and number of distinct items ordered).
***OrdersTable***
ID OrderNumber CustomerId Status created_at
1 123456 1 0 2018-01-01
2 234567 1 0 2018-01-02
3 345678 1 0 2018-01-03
4 456789 1 0 2018-01-04
***PurchasedProductsTable***
OrderId itemSku Qty
1 1000001 1
1 1000002 2
2 1000001 3
3 1000001 1
3 1000002 2
4 1000001 3
In the above table the query should first look at the oldest (created_at ASC) order (i.e with Id 1) having status 0 (in order table). and along with that order it should retrieves all the other orders that matches the same itemSku, qty and total distinct items count (in purchasedProducts table).
here order 1 and 3 matches the same itemSKu (1000001 and 1000002) and qty ( 1 and 2) and both have (2) distinct items count respectively so order 1 and 3 should be retrived at first.and when i marked order 1 and 3 as shipped (i.e chang status to 2).
and if i run query again it should retrive similar oders. now order 2 and 4 as order 2 and 4 are similar orders. (have same itemSkus (1000001, Qty (3) and distinct items count (1)).
please help thanks
You have to go trough your tables two times :)
Something like this :
SELECT DISTINCT O2.ID
FROM OrdersTable O1
INNER JOIN PurchasedProductsTable P1 ON O1.ID = P1.OrderId
INNER JOIN PurchasedProductsTable P2 ON P1.itemSku = P2.itemSku
AND P1.Qty = P2.Qty
INNER JOIN OrdersTable O2 ON O2.ID = P2.OrderId
WHERE O1.ID =
(SELECT ID FROM OrdersTable WHERE Status = 0
ORDER BY created_at ASC LIMIT 1)
AND (SELECT COUNT(*) FROM PurchasedProductsTable WHERE OrderId = O1.ID)
= (SELECT COUNT(*) FROM PurchasedProductsTable WHERE OrderId = O2.ID)
ORDER BY O2.ID ASC;
https://www.db-fiddle.com/f/65t9GgSfqMpzNVgnrJp2TR/2
You can get the earliest order via a limit and ordered by the date.
Then you can left join to get that order and any other order that at least has the same items.
Then once you have those order id's from the sub-query result, you can get the order details.
SELECT o.*
FROM
(
SELECT DISTINCT ord2.ID as OrderId
FROM
(
SELECT ID, CustomerId, Status
FROM OrdersTable
WHERE Status = 0
ORDER BY created_at
LIMIT 1
) AS ord1
JOIN PurchasedProductsTable AS pprod1
ON pprod1.OrderId = ord1.ID
LEFT JOIN OrdersTable ord2
ON ord2.CustomerId = ord1.CustomerId
AND ord2.Status = ord1.Status
LEFT JOIN PurchasedProductsTable pprod2
ON pprod2.OrderId = ord2.ID
AND pprod2.itemSku = pprod1.itemSku
AND pprod2.Qty = pprod1.Qty
GROUP BY ord1.CustomerId, ord1.ID, ord2.ID
HAVING COUNT(pprod1.itemSku) = COUNT(pprod2.itemSku)
) q
JOIN OrdersTable AS o ON o.ID = q.OrderId;
Test on RexTester here

Select last N rows following a condition

I've got a database table with logs which has 3 columns:
date | status | projectId
status can be either 0 or 1, primary key is on date and projectID
I'm trying to find out how many times a projectID had status 0 since the last time it was 1.
so if there would be only one projectId
date | status | projectId
1 0 3
2 0 3
3 1 3
4 1 3
5 0 3
6 0 3
this should return 2 (row 5 and 6 are 0 and row 4 is 1)
The thing that makes it hard for me is that I have to maintain the order of date. What would be a good way to tackle such problems, and this one in particular?
Here is how you would do it for one project:
select count(*)
from logs l
where status = 0 and
projectid = 3 and
date > (select max(date) from logs where projectid = 3 and status = 1)
Here is how you would do it for all projects:
select l.projectId, count(l1.projectId)
from logs l left outer join
(select projectId, max(date) as maxdate
from logs
where status = 1
group by projectId
) l1
on l.projectId = l1.projectId and
l.date > l1.date and
l.status = 0
group by l.projectId;
here you have an option in just one select.
http://sqlfiddle.com/#!2/6ce87/11
select *
from logs
where status=0 and date > (select date from logs where status=1 order by date desc limit 1)
Here's one way to get the result for all project_id:
SELECT m.project_id
, COUNT(1) AS mycount
FROM ( SELECT l.project_id
, MAX(l.date) AS latest_date
FROM mytable l
WHERE l.status = 1
) m
JOIN mytable t
ON t.project_id = m.project_id
AND t.date > m.latest_date
AND t.status = 0
If you need only a subset of project_id, the predicate should be added to the WHERE clause in the inline view query:
WHERE l.status = 1
AND l.project_id IN (3,5,7)
EDIT
That query does not return a row if there is no status=0 row after the latest status=1 row. To return a zero count, this could be done with an outer join.
SELECT m.project_id
, COUNT(t.status) AS mycount
FROM ( SELECT l.project_id
, MAX(l.date) AS latest_date
FROM mytable l
WHERE l.status = 1
AND l.project_id IN (3)
) m
LEFT
JOIN mytable t
ON t.project_id = m.project_id
AND t.date > m.latest_date
AND t.status = 0
For optimum performance, the statement could make use of an index with leading columns of project_id and date (in that order) and including the status column, e.g.
ON mytable (`project_id`,`date`,`status`)

mySQL - GROUP BY but get the most recent row

I've got a budget table:
user_id product_id budget created
-----------------------------------------------------------------
1 1 300 2011-12-01
2 1 400 2011-12-01
1 1 500 2011-12-03
2 2 400 2011-12-04
I've also got a manager_user table, joining a manager with the user
user_id manager_id product_id
------------------------------------
1 5 1
1 9 2
2 5 1
2 5 2
3 5 1
What I'd like to do is grab each of the user that's assigned to Manager #5, and also get their 'budgets'... but only the most recent one.
Right now my statement looks like this:
SELECT * FROM manager_user mu
LEFT JOIN budget b
ON b.user_id = mu.user_id AND b.product_id = mu.product_id
WHERE mu.manager_id = 5
GROUP BY mu.user_id, mu.product_id
ORDER BY b.created DESC;
The problem is it doesn't pull the most recent budget. Any suggestions? Thanks!
To accomplish your task you can do as follows:
select b1.user_id,
b1.budget
from budget b1 inner join (
select b.user_id,
b.product_id,
max(created) lastdate
from budget b
group by b.user_id, b.product_id ) q
on b1.user_id=q.user_id and
b1.product_id=q.product_id and
b1.created=q.lastdate
where b1.user_id in
(select user_id from manager_user where manager_id = 5);
I'm assuming here that your (user_id, product_id, created) combination is unique.
For what it's worth, here's the code that returned what I was looking for:
SELECT DISTINCT(b1.id),mu.user_id,mu.product_id,b1.budget,b1.created
FROM budget b1
INNER JOIN (
SELECT b.user_id, b.product_id, MAX(created) lastdate
FROM budget b
GROUP BY b.user_id, b.product_id) q
ON b1.user_id=q.user_id AND
b1.product_id=q.product_id AND
b1.created=q.lastdate
RIGHT JOIN manager_user mu
ON mu.user_id = b1.user_id AND
mu.product_id = b1.product_id
WHERE mu.manager_id = 5;
Thanks for the help Andrea!