MySQL - Perform checks on groupings of records - mysql

I have a result set like this:
"id","reference_id","type"
"1","aaa","A"
"2","aab","M"
"3","aac","A"
"4","aaa","M"
"5","aaa","E"
"6","aab","M"
"7","aac","M"
"8","aab","D"
"9","aac","M"
I want to run some validations on it using sql.
Each reference_id life cycle must contain only one "A";
Each reference_id life cycle must conatin at least 1 "E" and/or 1 "D";
How can I iterate through a resultset and perform checks per group of records (reference_id).

(Updated) Try:
select r.*
from my_result_set r
join (select reference_id
from my_result_set
group by reference_id
having sum(case type when 'A' then 1 end) = 1 and
sum(case when type in ('D','E') then 1 end) >= 1) s
on r.reference_id = s.reference_id
SQLFiddle here.

Related

Count Case Statement - When One Field Greater Than Another

I'm trying to determine how pervasive a particular mistake is in my database. I'm comparing one field against another, and when that field is greater then the other, I want it to count it. I'm also grouping it by a different statement. The purpose of this query is to determine where there are cases in my data base when one price field is larger then another.
The part of the query that is causing problems is "COUNT(CASE when p.IMAP > p.MSRP = 1 ELSE NULL END)" in the select statement. I put two little stars around it, hoping that'd help highlight where it is.
select b.brandName, b.BrandCode, p.ProductVendorStockNumber, **COUNT(Case When p.IMAP > p.MSRP = 1 ELSE NULL END) as 'Count'**
from products p
join brands b on p.brandID = b.brandID
where b.assignedTo = 'Steve' and p.IMAP > p.MSRP and status = 1
GROUP BY b.BrandName
For the count value You could use sum instead of count adding 1 when the condition is true and 0 when false
In sql for aggregated select the select for columns not in aggregated function and not mentioned in group by is deprecated, in the most recent version of mmysql is not allowed and for the older version the result for these values in unpredicatble so you should in group by then column that you have not in aggregation function in select eg:
select b.brandName
, b.BrandCode
, p.ProductVendorStockNumber
,sum(Case When p.IMAP > p.MSRP THEN 1 ELSE 0 END) as my_count
from products p
join brands b on p.brandID = b.brandID
where b.assignedTo = 'Steve' and p.IMAP > p.MSRP and status = 1
GROUP BY b.BrandName, b.BrandCode, p.ProductVendorStockNumber
or filter the result using the rows without aggregation and a join on the right aggregated rows

mysql count multiple types with a single query

I have a table "users" where each user has a column "status" containing the value "1" or "2" or "3";
Now with a single mysqli query, I just want to count how many users with status = 1, how many users with status = 2, and how many users with status = 3
NB: The query must be count ONLY and no data has to selected or output except the number of each status.
I found other questions here covering nearly the same topic but they are not exactly the same.
Thanks.
How about this:
SELECT
count(case when status = 1 then 1 else null end) AS STATUS_1,
count(case when status = 2 then 1 else null end) AS STATUS_2,
count(case when status = 3 then 1 else null end) AS STATUS_3
FROM users;
count will only count non-null entries and case-when will work on other databases as well.
This can be done by selectivity counting: just use a CASE expression inside count that only passed a non-null value for the desired rows:
SELECT count(CASE WHEN status = 1 THEN 1 END) status_1
, count(CASE WHEN status = 2 THEN 1 END) status_2
, count(CASE WHEN status = 3 THEN 1 END) status_3
FROM users
This is the same as the nicer, but not nativley supported syntax using FILTER:
SELECT count(*) FILER(WHERE status = 1) status_1
, count(*) FILER(WHERE status = 2) status_2
, count(*) FILER(WHERE status = 3) status_3
FROM users
Read more about this on my website modern SQL: http://modern-sql.com/feature/filter
Use something like this:
SELECT
SUM(IF(value=1,1,0)) as one,
SUM(IF(value=2,1,0)) as two,
SUM(IF(value=3,1,0)) as trhee
FROM users
The IF gives only a '1' back when your values is what you want it to be.

Query to get different values depending on all column values

I have a table tasks with a status column The value of status can either be 0,1 or 2.
I need to implement the following logic:
If any value in status is 0 then the answer is 0.
If all values are 2 then the result is 2.
Otherwise the result is one.
Right now I'm doing this with 3 queries.
a) SELECT status FROM tasks
b) SELECT status FROM tasks WHERE status = 2;
c) SELECT status FROM tasks WHERE status = 0
So a) is for getting the total number of rows. Then I compare that to the number of rows given by b). If the match then the answer is 2. If they don't I do query c). If it is non-empty the answer is 0 otherwise the answer is 1.
Is there any way to write a single query for this? Or is there a better way of doing it?
You can do it using conditional aggregation:
SELECT CASE
WHEN s0 >= 1 THEN 0
WHEN sAll = s2 THEN 2
ELSE 1
END
FROM (
SELECT COUNT(*) AS sAll,
COUNT(CASE WHEN status = 0 THEN 1 END) AS s0,
COUNT(CASE WHEN status = 2 THEN 1 END) AS s2
FROM tasks ) t
The sub-query performs all counts required to apply your business logic. The outer query simply uses this info, to produce the required result.
It's a bit unclear from your description whether the first condition in the CASE takes precedence over the second. If not, then just switch the position of the two WHENs.
Try:
SELECT MIN(status) FROM tasks;
- since when all status values are 2, the minimum will be 2, when any value is 0 then the minimum will be 0 and otherwise the minimum wll be 1.
Use following
SELECT (case
when ((SELECT count(*) FROM tasks WHERE status = 0)>0) then 0
when ((SELECT count(*) FROM tasks WHERE status = 2)=(SELECT count(*) FROM tasks)) then 2
else 1 end) AS countOfStatus

same (sub)query multiple time in a mysql query

I have a mysql query like the following.
select new,processing,close
from
(select count(id) new from tickets where id_client in (_a_list_of_client_id) and status = new),
(select count(id) processing from tickets where id_client in (_a_list_of_client_id) and status = processing),
(select count(id) close from tickets where id_client in (_a_list_of_client_id) and status = close)
The following is not the exact query but a pseudo query
here _a_list_of_client_id is another query like following
select id_client from client where id_user = {some_id_given_as_parameter}
I just wondering is this the right approach to use same subquery multiple times in a query. Or is there any other way to do things like this.
Thanks in advance
M H Rasel
You can use sum with case and move the subquery to the where criteria:
select
sum(case when status = 'new' then 1 else 0 end) new,
sum(case when status = 'processing' then 1 else 0 end) processing,
sum(case when status = 'close' then 1 else 0 end) close
from tickets
where id_client in (_a_list_of_client_id)
There are a couple other ways to do this (using if for example or leaving out the case), but I think this is easy to read. I believe mysql will work with sum(status='new') for example.

Alternative to "IN" that works like "AND" instead of "OR"

From my understanding, IN works like this:
$arrayName = array(1, 2, 3);
SELECT *
FROM tableName
WHERE productID IN ($arrayName)
is the equivalent of:
SELECT *
FROM tableName
WHERE productID = 1 OR productID = 2 OR productID = 3
I'm wondering if there's a SQL function that works like IN but uses AND in place of OR to compare to an array. Something that would expand to this:
SELECT *
FROM tableName
WHERE productID = 1 AND productID = 2 AND productID = 3
Not that it's necessary, but for context I'm simply creating a sort list for some search results that are being populated on a PHP page via jQuery. I can do what I need with PHP, I'll simply create the query dynamically depending on what options the user has selected, but I'd rather use an intelligent SQL function if possible.
***EDIT: Thanks everyone for the help. I explained my problem very poorly and you were still able to sort it out, which I appreciate. I found that someone else had asked this question more eloquently and received an answer that I can use:
Is there something in MySQL like IN but which uses AND instead of OR?
I'm trying to figure out how to accept an answer and close this but I'm having a bit of trouble...
You cannot possibly do this,
SELECT *
FROM tableName
WHERE productID = 1 AND productID = 2 AND productID = 3
the condition will always returns false because a row can have only one value on its column, the alternative way to do this is by grouping the result, ex.
SELECT colName
FROM tableName
WHERE productID IN (1,2,3)
GROUP BY colName
HAVING COUNT(DISTINCT colName) = 3
by having a condition HAVING COUNT(DISTINCT colName) = 3, this means that the instance of a record must be equal to the total count of parameters supplied on IN clause.
As written, your query will produce no rows. It is not possible for productID in a row to be equal to both 1 and 2 at the same time.
You are probably looking for a group of rows that contain these three products. Say you want to find orders that have all three products. You can use something like:
select orderid
from orderlines ol
group by orderid
havnig max(case when ol.productid = 1 then 1 else 0 end) > 0 and
max(case when ol.productid = 2 then 1 else 0 end) > 0 and
max(case when ol.productid = 3 then 1 else 0 end) > 0
The GROUP BY with the HAVING clause will find orders where all three products are present.
SELECT orderid
FROM tableName
WHERE productID IN (1, 2, 3)
GROUP BY orderid
HAVING COUNT(DISTINCT productID) = 3 --this number must match the number of unique IDs in the IN clause