MySQL: Joined table columns without a certain column value - mysql

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

Related

Can someone optimize this SQL query?

I am currently working on a project that has 2 very large sql tables Users and UserDocuments having around million and 2-3 millions records respectively. I have a query that will return the count of all the documents that each indvidual user has uploaded provided the document is not rejected.
A user can have multiple documents against his/her id.
My current query:-
SELECT
u.user_id,
u.name,
u.date_registered,
u.phone_no,
t1.docs_count,
t1.last_uploaded_on
FROM
Users u
JOIN(
SELECT user_id,
MAX(updated_at) AS last_uploaded_on,
SUM(CASE WHEN STATUS != 2 THEN 1 ELSE 0 END) AS docs_count
FROM
UserDocuments
WHERE
user_id IN(
SELECT
user_id
FROM
Users
WHERE
region_id = 1 AND city_id = 8 AND user_type = 1 AND user_suspended = 0 AND is_enabled = 1 AND verification_status = -1
) AND document_id IN('1', '2', '3', '4', '10', '11')
GROUP BY
user_id
ORDER BY
user_id ASC
) t1
ON
u.user_id = t1.user_id
WHERE
docs_count < 6 AND region_id = 1 AND city_id = 8 AND user_type = 1 AND user_suspended = 0 AND is_enabled = 1 AND verification_status = -1
LIMIT 1000, 100
Currently the query is taking very long around 20 secs to return data with indexes. can someone suggest some tweaks in the follwing query to gain some more preformance out of it.
SELECT
u.user_id,
max( u.name ) name,
max( u.date_registered ) date_registered,
max( phone_no ) phone_no,
MAX(d.updated_at) last_uploaded_on,
SUM(CASE WHEN d.STATUS != 2
THEN 1 ELSE 0 END) docs_count
FROM
Users u
JOIN UserDocuments d
ON u.user_id = d.user_id
AND d.document_id IN ('1', '2', '3', '4', '10', '11')
WHERE
u.region_id = 1
AND u.city_id = 8
AND u.user_type = 1
AND u.user_suspended = 0
AND u.is_enabled = 1
AND u.verification_status = -1
GROUP BY
u.user_id
HAVING
SUM(CASE WHEN d.STATUS != 2
THEN 1 ELSE 0 END) < 6
ORDER BY
u.user_id ASC
LIMIT
1000, 100
Have indexes on your tables as
user ( region_id, city_id, user_type, user_suspended, is_enabled, verification_status )
UserDocuments ( user_id, document_id, status, updated_at )
You are adding extra querying from the user table to both the inner and outer joins which might be killing it. Having an index on your critical "WHERE" components by user will pre-filter that set out. Only from that will it join to the UserDocuments table. By having the outer query get the counts() at the top level query.
Since the users name, registered and phone dont change per user, applying max() to each respectively prevents the need of adding those columns to the group by clause.
The index on the documents table on only the columns needed to confirm status and document_id and when last updated. This prevents the engine from having to go to the raw data pages as it can get the qualifying details directly from the index parts saving you time too.
LIMIT without ORDER BY does not make sense.
An ORDER BY in a 'derived table' is ignored.
Will you really have thousands of result rows? (I see the "offset of 1000".)
Use JOIN instead of IN ( SELECT ... )
What indexes do you have? I suggest INDEX(region_id, city_id, user_id)
CASE WHEN d.STATUS != 2 THEN 1 ELSE 0 END can be shortened to d.status != 2.
How many different values of status are there? If only two, then flip the test to d.status = 1`.

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'
...

How to split SQL query results into columns based on two WHERE conditions and two calculated COUNT fields?

I have the following (simplified) database schema:
Persons:
[Id] [Name]
-------------------
1 'Peter'
2 'John'
3 'Anna'
Items:
[Id] [ItemName] [ItemStatus]
-------------------
10 'Cake' 1
20 'Dog' 2
ItemDocuments:
[Id] [ItemId] [DocumentName] [Date]
-------------------
101 10 'CakeDocument1' '2016-01-01 00:00:00'
201 20 'DogDocument1' '2016-02-02 00:00:00'
301 10 'CakeDocument2' '2016-03-03 00:00:00'
401 20 'DogDocument2' '2016-04-04 00:00:00'
DocumentProcessors:
[PersonId] [DocumentId]
-------------------
1 101
1 201
2 301
I have also set up an SQL fiddle to play with: http://www.sqlfiddle.com/#!3/e6082
The relation logic is the following: every Person can work on zero or infinite number of ItemDocuments (many-to-many); each ItemDocument belongs to exactly one Item (one-to-many). Item has status 1 - Active, 2 - Closed
What I need is a report that fulfills the following requirements:
for each person in Persons table, display count of Items that have ItemDocuments related to this person
the counts should be split in two columns by ItemStatus
the query should be filterable by two optional date periods (using two BETWEEN conditions on ItemDocuments.Date field) and the Item counts should also be split into two periods
if a Person does not have any ItemDocuments assigned, it still should be shown in the results with all count values set to 0
if a Person has more than one ItemDocument for an Item, the Item still should be counted only once
Essentially, here is how the results should look like if I use both periods to NULL (to read all the data):
[PersonName] [Active Items for period 1] [Closed Items for period 1] [Active Items for period 2] [Closed Items for period 2]
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
'Peter' 1 1 1 1
'John' 1 0 1 0
'Anna' 0 0 0 0
While I can create an SQL query for each requirement separately, I have a problem to understand how to combine all of them together into one.
For example, I can split ItemStatus counts in two columns using
COUNT(CASE WHEN t.ItemStatus = 1 THEN 1 ELSE NULL END) AS Active,
COUNT(CASE WHEN t.ItemStatus = 2 THEN 1 ELSE NULL END) AS Closed
and I can filter by two periods (with max/min date constants from MS SQL server specification to avoid NULLs for optional period dates) using
between coalesce(#start1, '1753-01-01') and coalesce(#end1, '9999-12-31')
between coalesce(#start2, '1753-01-01') and coalesce(#end2, '9999-12-31')
but how to combine all of this together, considering also JOINs between tables?
Is there any technique, join or MS SQL Server specific approach to do this in efficient way?
My first attempt seems to work as required but it looks like ugly subquery duplications multiple times:
DECLARE #start1 DATETIME, #start2 DATETIME, #end1 DATETIME, #end2 DATETIME
-- SET #start2 = '2017-01-01'
SELECT
p.Name,
(SELECT COUNT(1)
FROM Items i
WHERE i.ItemStatus = 1 AND EXISTS(
SELECT 1
FROM DocumentProcessors AS dcp
INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
AND idc.Date BETWEEN COALESCE(#start1, '1753-01-01') AND COALESCE(#end1, '9999-12-31')
)
) AS Active1,
(SELECT COUNT(*)
FROM Items i
WHERE i.ItemStatus = 2 AND EXISTS(
SELECT 1
FROM DocumentProcessors AS dcp
INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
AND idc.Date BETWEEN COALESCE(#start1, '1753-01-01') AND COALESCE(#end1, '9999-12-31')
)
) AS Closed1,
(SELECT COUNT(1)
FROM Items i
WHERE i.ItemStatus = 1 AND EXISTS(
SELECT 1
FROM DocumentProcessors AS dcp
INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
AND idc.Date BETWEEN COALESCE(#start2, '1753-01-01') AND COALESCE(#end2, '9999-12-31')
)
) AS Active2,
(SELECT COUNT(*)
FROM Items i
WHERE i.ItemStatus = 2 AND EXISTS(
SELECT 1
FROM DocumentProcessors AS dcp
INNER JOIN ItemDocuments AS idc ON dcp.DocumentId = idc.Id
WHERE dcp.PersonId = p.Id AND idc.ItemId = i.Id
AND idc.Date BETWEEN COALESCE(#start2, '1753-01-01') AND COALESCE(#end2, '9999-12-31')
)
) AS Closed2
FROM Persons p
I'm not absolutely sure if I really got what you want, but you might try this
WITH AllData AS
(
SELECT p.Id AS PersonId
,p.Name AS Person
,id.Date AS DocDate
,id.DocumentName AS DocName
,i.ItemName AS ItemName
,i.ItemStatus AS ItemStatus
,CASE WHEN id.Date BETWEEN COALESCE(#start1, '1753-01-01') AND COALESCE(#end1, '9999-12-31') THEN 1 ELSE 0 END AS InPeriod1
,CASE WHEN id.Date BETWEEN COALESCE(#start2, '1753-01-01') AND COALESCE(#end2, '9999-12-31') THEN 1 ELSE 0 END AS InPeriod2
FROM Persons AS p
LEFT JOIN DocumentProcessors AS dp ON p.Id=dp.PersonId
LEFT JOIN ItemDocuments AS id ON dp.DocumentId=id.Id
LEFT JOIN Items AS i ON id.ItemId=i.Id
)
SELECT PersonID
,Person
,COUNT(CASE WHEN ItemStatus = 1 AND InPeriod1 = 1 THEN 1 ELSE NULL END) AS ActiveIn1
,COUNT(CASE WHEN ItemStatus = 2 AND InPeriod1 = 1 THEN 1 ELSE NULL END) AS ClosedIn1
,COUNT(CASE WHEN ItemStatus = 1 AND InPeriod2 = 1 THEN 1 ELSE NULL END) AS ActiveIn2
,COUNT(CASE WHEN ItemStatus = 2 AND InPeriod2 = 1 THEN 1 ELSE NULL END) AS ClosedIn2
FROM AllData
GROUP BY PersonID,Person

count not null columns out of specific list of columns and then compare in where clause for each row

Table structure:
Table Structure http://imagebin.org/index.php?mode=image&id=238883
I want to fetch data from both the tables which satisfy some of the conditions like
WHERE batch='2009', sex='male',course='B.Tech', branch='cs', xth_percent>60,
x2percent>60, gradpercent>60 and (if ranktype='other' ) then
no._of_not_null_semester>number elseifranktype='Leet') then
no._of_not_null_semester>number-2
sem 1-8 contains percentage for 8 semesters, and I want to filter results for each student if they have cleared 3 semesters or 4 semester i.e. not null values out of 8 values
no._of_not_null_semester
needs to be calculated, it is not a part of database, need help with that as well.
Required Query
SELECT * FROM student_test
INNER JOIN master_test ON student_test.id=master_test.id
WHERE sex='male' and batch='2009' and course='B.Tech'
and xthpercent>60 and x2percent>60 and
WHEN ranktype='Leet' THEN
SUM(CASE WHEN sem1 IS NOT NULL THEN 1 ELSE 0
WHEN sem2 IS NOT NULL THEN 1 ELSE 0
WHEN sem3 IS NOT NULL THEN 1 ELSE 0
WHEN sem4 IS NOT NULL THEN 1 ELSE 0
WHEN sem5 IS NOT NULL THEN 1 ELSE 0) >2
ELSE
SUM(CASE WHEN sem1 IS NOT NULL THEN 1 ELSE 0
WHEN sem2 S NOT NULL THEN 1 ELSE 0
WHEN sem3 IS NOT NULL THEN 1 ELSE 0
WHEN sem4 IS NOT NULL THEN 1 ELSE 0
WHEN sem5 IS NOT NULL THEN 1 ELSE 0) >4
Without changing the structure you can't use COUNT to achieve this.
One way to solve the problem would be to create a semester table which would contain a row for each finished semester for each student. This would have a foreign key pointing to test_student.id and you could use COUNT(semester.id)
If that is an option for you, it would be the best solution.
EDIT:
Check this out, didn't test the query but should work generally
I decided to do the math in the select itself to prevent calculating the same thing twice.
The HAVING conditions are applied after your result is ready to deliver, just before a LIMIT.
In terms of speed optimization you could try and move the sSum block into the WHERE condition just like you had it before. Probably it doesn't make much of a difference
SUM() does not work because it is an aggregate function which summarizes values in a column
I did some changes to your query in addition:
don't SELECT *, select specific fields and add a table identifier. ( in this case I used the aliases s for student_test AND m for master_test )
you put s.batch = '2009' into single quotes - if the field batch is an integer field, you should use s.batch = 2009, which would prevent MySQL from casting every single row to string to be able to compare it (int = int much faster than cast(int as varchar) = varchar) same about the other numeric values in your table
The Query:
SELECT
s.id,
s.sex,
s.course,
s.branch,
(
IF ( m.sem1 IS NOT NULL, 1, 0 ) +
IF ( m.sem2 IS NOT NULL, 1, 0 ) +
IF ( m.sem3 IS NOT NULL, 1, 0 ) +
IF ( m.sem4 IS NOT NULL, 1, 0 ) +
IF ( m.sem5 IS NOT NULL, 1, 0 ) +
IF ( m.sem6 IS NOT NULL, 1, 0 ) +
IF ( m.sem7 IS NOT NULL, 1, 0 ) +
IF ( m.sem8 IS NOT NULL, 1, 0 )
) AS sSum
FROM
student_test s
INNER JOIN master_test m ON m.id = s.id
WHERE
s.sex = 'male'
AND
s.batch = '2009' # I dont see this field in your database diagram!?
AND
s.course = 'B.Tech'
AND
m.xthpercent > 60
AND
m.x2percent > 60
HAVING
(
m.ranktype = 'OTHER'
AND
sSum > 4
)
OR
(
m.ranktype = 'LEET'
AND
sSum > 2
)
If you're generally interested in learning database design and usage I found an very interesting opportunity for you.
Stanford University offers a free database class "Introduction to databases". This is free and will cost you approx. 2 hours a week for 3 weeks, final exam included.
https://class2go.stanford.edu/db/Winter2013/preview/
SELECT
s.id,
s.sex,
s.course,
s.deptt,
m1.id,
m1.xthpercent,
m1.x2percent,
m1.sem1,
m1.sem2,
m1.sem3,
m1.ranktype,
m1.sem4,
m1.sem5,
m1.sem6,
m1.sem7,
m1.sem8,
m1.sSum
FROM
student_test s
INNER JOIN(SELECT m.id,
m.xthpercent,
m.x2percent,
m.sem1,
m.sem2,
m.sem3,
m.ranktype,
m.sem4,
m.sem5,
m.sem6,
m.sem7,
m.sem8,
( IF ( ceil(m.sem1)>0, 1, 0 ) +
IF ( ceil(m.sem2)>0, 1, 0 ) +
IF ( ceil(m.sem3)>0, 1, 0 ) +
IF ( ceil(m.sem4)>0, 1, 0 ) +
IF ( ceil(m.sem5)>0, 1, 0 ) +
IF ( ceil(m.sem6)>0, 1, 0 ) +
IF ( ceil(m.sem7)>0, 1, 0 ) +
IF ( ceil(m.sem8)>0, 1, 0 )
) AS sSum FROM master_test m
WHERE m.xthpercent>60 and
m.x2percent>60
HAVING (m.ranktype='Leet' AND sSum>2 )
OR
(m.ranktype != 'Leet') AND sSum>4 )
as m1 ON m1.id = s.id
WHERE
s.sex='Male'
and
s.course='B.Tech'
and
s.deptt='ELE'
This is the query finally I'm using, Love that query :)

MySQL conditionally counting results

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