MySQL conditionally counting results - mysql

I have a query which returns the counts of several different types of records but I now need to further qualify the result set. I am curious if there is an elegant way to combine these statements into a single statement. Basically if column 2 is true increment ND_true and if column 2 is false increment ND_false instead.
sum(if(c.1 = 'ND' and c.2 is true, if(c.2 = 'P', 1, 0), 0)) as 'ND_true'
sum(if(c.1 = 'ND' and c.2 is false, if(c.2 = 'P', 1, 0), 0)) as 'ND_false'

Erm...
select count(*) from `tablename` where [something something something]
Seems like a much better alternative to what you're doing. Either that or you're not explaining very clearly what you are doing and what led you to the solution you have.

One alternative:
Select ...
, C.ND_True As ND_True
, C.ND_False As ND_False
From ...
Cross Join (
Select Sum( Case When C1.P = 1 Then 1 Else 0 End ) As ND_True
, Sum( Case When C1.P = 0 Then 1 Else 0 End ) As ND_False
From SomeTable As C1
Where C1.1 = 'ND'
And C1.P = 'P'
Union All
Select Z.Val, Z.Val
From ( Select 0 As Val ) As Z
Where Not Exists (
Select 1
From SomeTable As C2
Where C2.1 = 'ND'
And C2.P = 'P'
)
) As C

Your query sample although brief is unclear... you are first testing of c1 = 'ND' (string comparison) ANDed with c.2 (implying c.2 is logical), then another if( c.2 = 'P'... ) I'm sure you are abbreviating column names, but this isn't making sense. Is c.2 a logical field or a string field... one or the other.
sum(if(c.1 = 'ND' and c.2 is true, if(c.2 = 'P', 1, 0), 0)) as 'ND_true'
sum(if(c.1 = 'ND' and c.2 is false, if(c.2 = 'P', 1, 0), 0)) as 'ND_false'
Here is a simplified version of what I think you are looking for.. In this case, you are concerned with c.1 being "ND", so put that as your WHERE clause to limit what is retrieved from the table. Then you don't have to re-duplicate it as part of the IF() clause test. Then, just put in the "other" criteria where I have the c.2 expression... Since the clause is identical for what is being tested, the 2nd and 3rd columns trigger which column they will be counted in...
select
sum( if( c.2, 1, 0 )) as ND_True,
sum( if( c.2, 0, 1 )) as ND_False
from
yourTable c
where
c.1 = 'ND'
Ex: Data
col1 col2
AX true
BC true
ND true <-- this row
XY false
ND true <-- this row
ND false <-- this row
AX false
ND true <-- this row
would result in only the 4 marked rows being queried with a final count of
ND_True = 3
ND_False = 1

Related

Using Case When in SQL to return different values

I would like to join various table and convert some column values from integer to text like below case. Let's say I have Type_ID: 1, 2, 100, 200, 300, .....
If the Type_ID are 1 and 200, then it will print out High and Good respectively. But if the Type_ID are not 1 and 200, I want it to return original integer value. How can I do that? Thanks.
SELECT A.AID,
CASE WHEN B.Type_ID = '1' THEN 'High' WHEN B.Type_ID = '200' THEN 'GOOD' ELSE (Print Original number value) END AS 'Type'
FROM A, B
WHERE A.AID = B.AID
Expected Result:
AID
Type
1
High
2
499
3
Good
4
Good
5
100
6
300
If I understand correctly, the else value should just be the Type_ID:
SELECT A.AID,
CASE B.Type_ID WHEN '1' THEN 'High'
WHEN '200' THEN 'GOOD'
ELSE CAST(B.Type_ID AS CHAR(50)) END AS Type
FROM A
INNER JOIN B ON A.AID = B.AID;

MySQL select boolean fields and create 3rd states

I'm using MySQL and I want to get a boolean with 3 states (-1,0,1) on existing fields.
E.g if I have col1 = null or row doesn't exist col2 = 0 col3 = 1
Then I do :
SELECT col1, col2, col3 FROM customers
I want to get that :
Output
col1 = -1
col2 = 0
col3 = 1
How it's possible ?
note : In my code I'll use LEFT JOIN in my query so when the row doesn't exist so I think I'll get null.
Something like this:
SELECT
a.a,
COALESCE(b.b, -1) as b
FROM
a LEFT JOIN B ON a.id = b.aid
Suppose we have tables:
A.id, A.a
1, 'hello'
2, 'goodbye'
3, 'what'
B.aid, b.b
1, 1
2, null
When we join them with left join we will get:
A.id, a.a, B.aid, b.b
1, 'hello', 1, 1
2, 'goodbye', 2, null --b.b is null because the row value is null
3, 'what', null, null --b.b is null because there is no row, all b columns are null
COALESCE looks at the arguments in order from left to right, returning the first non null one. If the row in b doesn't exist, or it does exist but b.b is NULL in the table, then COALESCE will turn it into -1:
A.id, a.a, B.aid, b.b
1, 'hello', 1, 1
2, 'goodbye', 2, -1 --b.b is -1 because COALESCE found -1 as the first non null value
3, 'what', null, -1 --b.b is -1 because COALESCE found -1 as the first non null value
If however you want to use 0 if b.b is null but -1 if b.b doesn't exist, you need to CASE WHEN using something that is a value:
SELECT
a.a,
CASE WHEN b.aid IS NULL THEN -1 ELSE COALESCE(b.b, 0) END as b
FROM
a LEFT JOIN B ON a.id = b.aid
Here we inspect one of the columns used in the join, b.aid. If it is null we know the join failed and there is no row in B for that row in A, hence we can CASE WHEN test b.aid for being null and if it is, then put -1 as our "-1 means no row was in b", ELSE we can COALESCE to turn b.b into 0 if it is null. This gives a result like:
A.id, a.a, B.aid, b.b
1, 'hello', 1, 1
2, 'goodbye', 2, 0 --b.b is 0 because CASE WHEN's ELSE block was used, and COALESCE found 0 as the first non null value
3, 'what', null, -1 --b.b is -1 because CASE WHEN's WHEN block fired, because b.aid was null, and CASE WHEN provided -1 as the value

Adding two columns in mysql distinguish the count of male and female

I am trying to add two new columns to my query to be able to get the count of males and females
SELECT adggeth.reg01_maininfo.techname AS TechName
,adggeth.reg01_maininfo.techmobile AS Mobile
,monthname(adggeth.lng02_rpt_b_calvedets.calvdatealv) AS "Calving Month"
,count(adggeth.lng02_rpt_b_calvedets.sex) AS "No of Calves"
FROM adggeth.reg01_maininfo
INNER JOIN adggeth.lng02_maininfo ON adggeth.reg01_maininfo.techmobile = adggeth.lng02_maininfo.aitechid
INNER JOIN adggeth.lng02_rpt_b_calvedets ON adggeth.lng02_maininfo.hh_id = adggeth.lng02_rpt_b_calvedets.hh_id
AND adggeth.lng02_maininfo.visitdate = adggeth.lng02_rpt_b_calvedets.visitdate
GROUP BY adggeth.reg01_maininfo.techname
,adggeth.reg01_maininfo.techmobile
,monthname(adggeth.lng02_rpt_b_calvedets.calvdatealv);
I need to count the number of my female and male calves where
lng02_rpt_b_calvedets.sex = 1 refers to male calves
lng02_rpt_b_calvedets.sex = 2 refers to female calves
Just found my answer
SELECT
adggeth.reg01_maininfo.techname AS TechName,
adggeth.reg01_maininfo.techmobile AS Mobile,
MONTHNAME(
adggeth.`serv00_rpt_calvdtls2`.`calvdatealv2`
) AS "Calving Month",
COUNT(
adggeth.`serv00_rpt_calvdtls2`.`sex2`
) AS "No of Calves",
COUNT(
IF (
adggeth.serv00_rpt_calvdtls2.sex2 = 2,
adggeth.serv00_rpt_calvdtls2.sex2,
NULL
)
) AS 'Femal Calf',
COUNT(
IF (
adggeth.serv00_rpt_calvdtls2.sex2 = 1,
adggeth.serv00_rpt_calvdtls2.sex2,
NULL
)
) AS 'Male Calf'
FROM
adggeth.reg01_maininfo
INNER JOIN adggeth.`serv00_maininfo`
ON adggeth.reg01_maininfo.techmobile = adggeth.`serv00_maininfo`.`aitechid`
INNER JOIN adggeth.`serv00_rpt_calvdtls2`
ON adggeth.`serv00_maininfo`.`fid` = adggeth.`serv00_rpt_calvdtls2`.`fid`
AND adggeth.`serv00_maininfo`.`regdate` = adggeth.`serv00_rpt_calvdtls2`.`calvdatealv2`
GROUP BY adggeth.reg01_maininfo.techname,
adggeth.reg01_maininfo.techmobile,
MONTHNAME(
adggeth.`serv00_rpt_calvdtls2`.`calvdatealv2`
)
You can use a count call on a case expression that filters the calves according to their sex:
COUNT (CASE adggeth.lng02_rpt_b_calvedets.sex WHEN 1 END) as "No of Male Calves",
COUNT (CASE adggeth.lng02_rpt_b_calvedets.sex WHEN 2 END) as "No of Female Calves"
You can also use COUNT aggregation with IF function:
COUNT (IF(adggeth.lng02_rpt_b_calvedets.sex = 1, 1, NULL) ) AS Male_Calves_Count,
COUNT (IF(adggeth.lng02_rpt_b_calvedets.sex = 2, 1, NULL) ) AS Female_Calves_Count,
While COUNT will not count NULLs, and therefore it is possible to use some condition which returns NULL if it's not the value you're looking for, it's much simpler to use SUM instead. Boolean expressions in MySQL return 1 or 0, so you can add up their values directly.
SELECT
SUM(adggeth.serv00_rpt_calvdtls2.sex2 = 2) AS 'Female Calf',
SUM(adggeth.serv00_rpt_calvdtls2.sex2 = 1) AS 'Male Calf'
...

Return 1 or 0 in SQL depending on the multiple statements

If I find that some of the user exists with such a parameters, I want to get 1 otherwise 0. In the future I'll have to add more blocks. But it doesn't seem to work now. What am I doing wrong?
SELECT CAST(CASE WHEN EXISTS(SELECT 1
FROM Customers
WHERE Country = 'France' AND PostalCode%2 = 0)
OR (WHERE Country = 'Germany' AND PostalCode%2 = 0))
)
THEN 1
ELSE 0
END AS BIT)
You need two separate exists:
SELECT CAST(CASE WHEN EXISTS (SELECT 1
FROM Customers
WHERE Country = 'France' AND PostalCode%2 = 0
)
THEN 1
WHEN EXISTS (SELECT 1
FROM Customers
WHERE Country = 'Germany' AND PostalCode%2 = 0
)
THEN 1
ELSE 0
END AS BIT)
Actually, I broke this into two separate THEN clauses. This is almost equivalent to using OR, but because the logic is inside a CASE, THEN seems more natural. (The difference is that the optimizer could choose to re-arrange the OR conditions, but the THEN conditions are executed in lexical order.)
If your statements are actually this simple, you can combine them as:
SELECT CAST(CASE WHEN EXISTS (SELECT 1
FROM Customers
WHERE Country IN ('France', 'Germany') AND PostalCode%2 = 0
)
THEN 1
ELSE 0
END AS BIT)
It looks to me like you're just having issues with your bracketing:
SELECT CAST(
CASE WHEN EXISTS(
SELECT 1
FROM Customers
WHERE (Country = 'France' AND PostalCode%2 = 0)
OR (Country = 'Germany' AND PostalCode%2 = 0)
) THEN 1 ELSE 0 END AS BIT)
Building on Gordon's assumption that PostalCode%2 = 0 for all tested 'sets' of conditionals (you haven't said as much yet), you could likewise shorten this to:
SELECT CAST(
CASE WHEN EXISTS(
SELECT 1
FROM Customers
WHERE PostalCode%2 = 0
AND Country IN ('France', 'Germany')
) THEN 1 ELSE 0 END AS BIT)

MySQL: Joined table columns without a certain column value

I have a table that stores file information called files. Each row in the files table can be related to multiple categories and tags.
I have created a table called file_relationships that stores the link between the file and its categories and tags.
Basically what I want to do is filter my results based on certain categories and tags.
So for example I might want to extract all files with the tag number 12 and 15 and files with category number 1 and 2.
However, let's say one of the files had the tag number 12 assigned to it but did not have any category relationships. I'd still want it to be included in the results.
I also want files without any tags and categories assigned to be included. I've acheived this by using fr.relationship IS NULL in my SQLFiddle link below.
I've created a quick mock up of the MySQL code I have been playing with. It's really bad code but hopefully it helps make sense of what I'm trying to achieve.
http://sqlfiddle.com/#!2/931f2/1
I think I have what you are looking for but let me obviously explain what is happening. I am pre-querying (alias PQ) only from the file relationships table an aggregate on each file ID. These aggregates are basically flags of 1 or 0 stating it does or not have something.
The result of the left-join, you want the entry if there are no relationships at all, so your IS NULL is obviously good to go.
Now, your consideration for a file that does not have ANY "TAG" or "CAT" entries. This is where the MAX( IF()) for HasOtherRelation comes in. Through all the entries for a given file ID, all I care about is "are there ANY entries other than cat or tag"... if so, set to 1, otherwise leave 0. Similarly a flag for "HasTagOrCat", so in my where clause I am looking for anything that has an other relation = 1 AND the Has Tag Or Cat flag is zero.
Finally, on to your requirements of meeting your consideration for tag or cat qualifiers but not if the file has other cat or tag entries. Similar MAX( IF()) for those.
select
f.id,
f.file_title
from
files f
LEFT JOIN
( select
fr.file_id,
max( if( fr.relationship IN ('tag','cat'), 1, 0 )) as HasTagOrCat,
max( if( fr.relationship NOT IN ('tag','cat'), 1, 0 )) as HasOtherRelation,
max( if( fr.relationship = 'tag' AND fr.relationship_id IN('12','15'), 1, 0 )) as HasTags,
max( if( fr.relationship = 'cat' AND fr.relationship_id IN('1','2'), 1, 0 )) as HasCats,
max( if( fr.relationship = 'tag' AND fr.relationship_id NOT IN('12','15'), 1, 0 )) as HasOtherTags,
max( if( fr.relationship = 'cat' AND fr.relationship_id NOT IN('1','2'), 1, 0 )) as HasOtherCats
from
file_relationships AS fr
group by
fr.file_id ) PQ
ON f.id = PQ.file_id
where
PQ.file_id IS NULL
OR ( PQ.HasOtherRelation = 1
AND PQ.HasTagOrCat = 0 )
OR ( PQ.HasTags + PQ.HasCats > 0
AND PQ.HasOtherTags + PQ.HasOtherCats = 0 )
So, at the end of the inner pre-query, your results would have the following
File_ID HasTagOrCat HasOtherRelation HasTags HasCats HasOtherTags HasOtherCats
1 1 0 1 1 0 0
2 1 0 1 1 1 1
3 1 0 1 0 0 0
So, your data didn't have any such non tag/cat considerations in the data sample, nor any NULL for no such file entry in relationship table, but the others as summarized above would result in only file 1 & 3 being included in the result set...
They each have AT LEAST a Tag or Cat you were looking for an NO OTHER tag/cat that you did NOT want. For entry 2, yes, it had both a tag and cat you were looking for, but ALSO had other tag/cats that you didnt so that is excluded from result.
replace and with or
--->
(fr.relationship = 'tag' AND fr.relationship_id IN('12','15'))
and (fr.relationship = 'cat' AND fr.relationship_id IN('1', '2'))
you are checking if fr.relationship = 'tag' and fr.relationship = 'cat'
fr.relationship can be 'cat' or fr.relationship can be 'tag' for 1 record/row
can't equal to both values
SELECT f.id,
f.file_title ,fr.relationship
FROM files AS f
LEFT JOIN file_relationships AS fr
ON f.id = fr.file_id
and /*REPLACEMENT as u want null column also */ (
fr.relationship IS NULL
OR (fr.relationship NOT IN ('tag','cat'))
OR (
(fr.relationship = 'tag' AND fr.relationship_id IN('12','15'))
or (fr.relationship = 'cat' AND fr.relationship_id IN('1', '2'))
)
)
GROUP BY f.id
EDIT---
IS THIS WHAT U WANT
http://sqlfiddle.com/#!2/df016d/4
SELECT f.id,
f.file_title ,group_concat(fr.relationship)
FROM files AS f
LEFT JOIN file_relationships AS fr
ON f.id = fr.file_id
and (
fr.relationship IS NULL
OR (fr.relationship NOT IN ('tag','cat'))
OR (
(fr.relationship = 'tag' AND fr.relationship_id IN('12','15'))
or (fr.relationship = 'cat' AND fr.relationship_id IN('1', '2'))
)
)
GROUP BY f.id