Where clause with having count breaks query - mysql

This query isn't returning what I though it should:
Select user_id
from subs
where status = 'Failed'
and next_run_dt = '2016-08-04'
and active = '1'
group by user_id having count(status) = 2
Table structure and sample data:
id__|__status__|__next_run_dt__|__user_id__|__active__|
| | | | |
1 | Failed | 2016-08-04 | 3 | 1 |
___|__________|_______________|___________|__________|
| | | | |
2 | Failed | 2016-08-04 | 4 | 1 |
___|__________|_______________|___________|__________|
| | | | |
3 | Failed | 2016-08-04 | 3 | 1 |
___|__________|_______________|___________|__________|
The query should return the user_id of 3 because that user has two entries with at status of Failed. The query returns the user_id if I remove and next_run_dt = '2016-08-04' (date column).
What am I missing?
EDIT: There also exists the possibility that a user_id can also have a next_run_dt of null as well

Are you sure it's a date column? If it's datetime, you'll have problems.
Your query works for me on SQLFiddle.
http://sqlfiddle.com/#!9/f0bab6/1

Related

How Affect Group By to Other Second Join Table

I have some table like this
table request_buys
| id | invoice | user_id |
| -- | ----------------- | ------- |
| 3 | 20220405/01104298 | 1 |
table traces
| id | request_buy_id | status_id | created_at |
| -- | -------------- | --------- | ------------------- |
| 37 | 3 | 1 | 2022-03-27 14:12:25 |
| 38 | 3 | 2 | 2022-03-28 14:12:25 |
| 39 | 3 | 3 | 2022-03-29 14:12:25 |
| 40 | 3 | 4 | 2022-03-30 14:12:25 |
| 41 | 3 | 5 | 2022-03-31 14:12:25 |
| 42 | 3 | 6 | 2022-04-01 14:12:25 |
table statuses
| id | nama |
| -- | ----------------- |
| 1 | Order Placed |
| 2 | Order Paid |
| 3 | Accepted |
| 4 | Picked by Courier |
| 5 | In Transit |
| 6 | Delivered |
| 7 | Rated |
| 8 | Rejected |
| 9 | Canceled |
and then i try to design query like below
select
request_buys.invoice,
MAX(traces.id) as traces_id,
MAX(statuses.nama) as statuses_nama
from
`request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
inner join `statuses` on `traces`.`status_id` = `statuses`.`id`
where
`user_id` = 1
group by
request_buys.id
and produces output like the following
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Picked by Courier |
and the output i expect should be like in the table below
expect
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Delivered |
I understand my error is in MAX(statuses.nama) which I should change like removing MAX() in statuses.nama
But i just get error like this "SELECT list is not in GROUP BY clause and contains nonaggregated ... this is incompatible with sql_mode=only_full_group_by"
then I tried some to clear the value "ONLY_FULL_GROUP_BY" with a query like the following
SET sql_mode=(SELECT REPLACE(##sql_mode,'ONLY_FULL_GROUP_BY',''))
and the result is like this
output
| invoice | traces_id | statuses_nama |
| ----------------- | --------- | ----------------- |
| 20220405/01104298 | 42 | Order Placed |
and I'm really stuck at this
and how to make trace_id.status_id from the "GROUP BY" result based on request_buys.id still have a relationship with statuses.id
Your problem lies with your misuse of the MAX(statuses.nama) expression. Based on your expected output,you intend to get the statuses.nama which matches the MAX(traces.id), NOT the MAX(statuses.nama) value which returns the highest value in terms of alphabetic order. In this case, the initial letter 'P' > 'D' . I have tweaked your code a bit and tried it on workbench,supposing there are more than one invoice for a particular user.(e.g insert into request_buys values (4,'20230405/01104298',1); insert into traces values (43,4,7,'2022-04-01 14:12:25');) It works as intended.
select invoice, t.id as traces_id, s.nama as statuses_name from request_buys r
join traces t on r.id=t.request_buy_id
join statuses s on t.status_id=s.id
join
(select traces.request_buy_id, MAX(traces.id) as traces_id
from `request_buys`
inner join `traces` on `request_buys`.`id` = `traces`.`request_buy_id`
where
`user_id` = 1
group by
traces.request_buy_id ) join_t
on t.request_buy_id=join_t.request_buy_id and t.id=join_t.traces_id
;
If I'm understanding correctly, you're trying to retrieve the most recent status for each invoice. Using MAX(nama) won't return that result, because it just picks the maximum status name alphabetically.
Assuming you're using MySQL 8.x, use ROW_NUMBER() to sort and rank the statuses for each invoice, by the most recent date first. Then grab the latest one using where rowNum = 1
WITH cte AS (
SELECT rb.id AS request_buy_id
, rb.invoice
, t.id AS traces_id
, s.nama AS statuses_nama
, ROW_NUMBER() OVER(PARTITION BY rb.id ORDER BY t.created_at DESC) AS RowNum
FROM request_buys rb
INNER JOIN traces t ON rb.id = t.request_buy_id
INNER JOIN statuses s ON t.status_id = s.id
WHERE user_id = 1
)
SELECT *
FROM cte
WHERE RowNum = 1
;
Result:
request_buy_id
invoice
traces_id
statuses_nama
RowNum
3
20220405/01104298
42
Delivered
1
db<>fiddle here

MySQL nested Select statement with subquery

I am struggeling with a database query for 2 Hours now.
There is the following database structure:
article table
+---------------+-------------+
| id | ordernumber |
+---------------+-------------+
| 1 | 3243 |
| 2 | 3344 |
| 3 | 3423 |
| 4 | 7687 |
+---------------+-------------+
variant table
+----+-----------+-------+-------+
| id | articleId | stock | price |
+----+-----------+-------+-------+
| 1 | 1 | 3 | 10,99 |
| 2 | 1 | 0 | 10,99 |
| 3 | 1 | 1 | 10,99 |
| 4 | 2 | 0 | 11,99 |
| 5 | 2 | 0 | 11,99 |
| 6 | 2 | 1 | 11,99 |
+----+-----------+-------+-------+
I want to get all Articles where all but one variant have 0 stock.
Is this even possible with a plain sql statement? I tried with a subquery, but without success, since the subquery gets executed first and I would need to pass values from the current record of the resultset of the outer query.
Any help is much appreciated.
Edit:
Expected Result:
+----+-------------+
| id | ordernumber |
+----+-------------+
| 2 | 3344 |
+----+-------------+
If you want the full information for the variant:
select v.*
from variants v
where v.stock > 0 and
not exists (select 1
from variants v2
where v2.articleID = v.articleID and
v2.stock > 0 and
v2.id <> v.id
);
Note: this assumes that the duplicated "5" is a typo and that the ids really are unique in the table.
This can be done using group by and having.
select articleID
from variants
group by articleID
having count(*) - 1 = count(case when stock = 0 then 1 end)

Selecting the most recent result from one table joining to another

I have two tables.
One table contains customer data, like name and email address. The other table contains a log of the status changes.
The status log table looks like this:
+-------------+------------+------------+
| customer_id | status | date |
+-------------+------------+------------+
| 1 | Bought | 2018-07-01 |
| 1 | Bought | 2018-07-02 |
| 2 | Ongoing | 2018-07-03 |
| 3 | Ongoing | 2018-07-04 |
| 1 | Not Bought | 2018-07-05 |
| 4 | Bought | 2018-07-06 |
| 4 | Not Bought | 2018-07-07 |
| 4 | Bought | 2018-07-08 | *
| 3 | Cancelled | 2018-07-09 |
+-------------+------------+------------+
And the customer data:
+-------------+------------+
| id | name | email |
+-------------+------------+
| 1 | Alex | alex#home |
| 2 | John | john#home |
| 3 | Simon | si#home |
| 4 | Philip | phil#home |
+-------------+------------+
I would like to select the customer's who have "Bought" in July (07). But exclude customers who's status has changed from "Bought" anything other most recently.
The result should be just one customer (Philip) - all the others have had their status change to something other than Bought most recently.
I have the following SQL:
SELECT
a.customer_id
FROM
statuslog a
WHERE
DATE(a.`date`) LIKE '2018-07-%'
AND a.status = 'Bought'
ORDER BY a.date DESC
LIMIT 1
But that is as far as I have got! The above query only returns one result, but essentially there could be more than one.
Any help is appreciated!
Here is an approach that uses a correlated subquery to get the most recent status record:
SELECT sl.customerid
FROM wwym_statuslog sl
WHERE sl.date = (SELECT MAX(sl2.date)
FROM wwym_statuslog sl2
WHERE sl2.customer_id = sl.customer_id AND
sl2.date >= '2018-07-01' AND
sl2.date < '2018-08-01'
) AND
sl.status = 'Bought'
ORDER BY sl.date DESC
LIMIT 1;
Notes:
Use meaningful table aliases! That is, abbreviations for the table names, rather than arbitrary letters such as a and b.
Use proper date arithmetic. LIKE is for strings. MySQL has lots of date functions that work.
In MySQL 8+, you would use ROW_NUMBER().

get result for 2 different tables in mysql

I am new to mysql. I have a requirement, please see the tables below
grand_score_master
autoid | user_id | package_id | grand_level | timestamp | timestring
55 | CBS_00002 | s78c_e4vt6 | 1 | ... | ...
58 | CBS_00002 | d47kndffqc | 3 | ... | ...
64 | CBS_00002 | d47kndffqc | 1 | ... | ...
65 | CBS_00002 | d47kndffqc | 2 | ... | ...
mega_score_master
autoid | user_id | package_id | mega_level | timestamp | timestring
1 | CBS_00002 | d47kndffqc | 1 | ... | ...
expected result
user_id | package_id | max_grand_leve | max_mega_level
CBS_00002 | s78c_e4vt6 | 1 | 0
CBS_00002 | d47kndffqc | 3 | 1
package_id and user_id is are in both tables.
I am trying to make a query where I will pass a user_id say 'CBS_00002' and query will return max(grand_level) and max(mega_level) by matching/grouping common package_id from both the table for that user.
I tried this
SELECT max(grand_score_master.grand_level),
grand_score_master.package_id,
max(mega_score_master.mega_level)
FROM grand_score_master
INNER JOIN mega_score_master
ON mega_score_master.package_id = grand_score_master.package_id
WHERE user_id='CBS_00002'
GROUP BY grand_score_master.package_id
But it only return result for d47kndffqc because s78c_e4vt6 is not present in mega_score_master
Need some help.
You're almost there, you only need to change your inner join to a left join; this will preserve all the rows from the first table, regardless of being matched in the second one or not:
SELECT max(t1.grand_level) max_grand_level,
t1.package_id,
IFNULL(max(t2.mega_level), 0) max_mega_level
FROM grand_score_master t1
LEFT JOIN
mega_score_master t2
ON t2.package_id = t1.package_id
WHERE t1.user_id='CBS_00002'
GROUP BY t1.package_id
I also added aliases to your tables, so that the query results a bit more compact.

How can I treat with NULL as minimum value?

I have a table like this:
// notifications
+----+-----------+-------+---------+---------+------+
| id | score | type | post_id | user_id | seen |
+----+-----------+-------+---------+---------+------+
| 1 | 15 | 1 | 2342 | 342 | 1 |
| 2 | 5 | 1 | 2342 | 342 | 1 |
| 3 | NULL | 2 | 5342 | 342 | 1 |
| 4 | -10 | 1 | 2342 | 342 | NULL |
| 5 | 5 | 1 | 2342 | 342 | NULL |
| 6 | NULL | 2 | 8342 | 342 | NULL |
| 7 | -2 | 1 | 2342 | 342 | NULL |
+----+-----------+-------+---------+---------+------+
-- type: 1 means "it is a vote", 2 means "it is a comment (without score)"
Here is my query:
SELECT SUM(score), type, post_id, seen
FROM notifications
WHERE user_id = 342
GROUP BY type, post_id
ORDER BY (seen IS NULL) desc
As you see, there is SUM() function, Also both type and post_id columns are in the GROUP BY statement. Well now I'm talking about seen column. I don't want to put it into GROUP BY statement. So I have to use either MAX() or MIN() for it. Right?
Actually I need to select NULL as seen column (in query above) if there is even one row which has seen = NULL. My current query selects 1 as seen's value, even when I use MIN(seen). So why 1 is minimum when there is NULL?
Also I want to order rows so that all SEEN = NULL be in the top of list. How can I do that?
Expected result:
// notifications
+-----------+-------+---------+------+
| score | type | post_id | seen |
+-----------+-------+---------+------+
| 13 | 1 | 2342 | NULL |
| NULL | 2 | 8342 | NULL |
| NULL | 2 | 5342 | 1 |
+-----------+-------+---------+------+
You could do this
case when sum(seen is null) > 0
then null
else min(seen)
end
You could use the following query:
SELECT SUM(score), type, post_id, min(IFNULL(seen, 0)) as seen
FROM notifications
WHERE user_id = 342
GROUP BY type, post_id
ORDER BY seen desc