Query to get percent of a total using select(count) - sql

I'm calculating the change in pain between day 1 and day 2.
There are two fields, Pain_Admit_Comfort and Pain_48_Hr_Comfort, the options in each is Yes/No.
I need to find everyone that had pain on Admit and is More Comfortable 2 days later.
This is the query. The first two statements return correct numbers. I can't figure out how to divide using the same statements as numerator and denominator.
select
(select COUNT (PAIN_48_HR_COMFORT_C)
FROM CASES WHERE PAIN_48_HR_COMFORT_C='Yes') as Forty_Eight_Hours,
(SELECT COUNT (PAIN_ADMIT_COMFORT_C)
FROM CASES WHERE PAIN_ADMIT_COMFORT_C='YES') as Admit_Uncomfort_Yes,
((select COUNT (PAIN_48_HR_COMFORT_C)
FROM CASES WHERE PAIN_48_HR_COMFORT_C='Yes')
/
(SELECT COUNT (PAIN_ADMIT_COMFORT_C)
FROM CASES WHERE PAIN_ADMIT_COMFORT_C='YES')) AS Percent_Changed
from CASES
Thanks

I don't spot any immediate problems with your statement but following statement should return the correct results and is perhaps a bit easier to read.
SELECT feh.Forty_Eight_Hours
, auy.Admit_Uncomfort_Yes
, Percent_Changed = CAST(feh.Forty_Eight_Hours AS FLOAT) / auy.Admit_Uncomfort_Yes
FROM (
SELECT Forty_Eight_Hours = COUNT(PAIN_48_HR_COMFORT_C)
FROM CASES
WHERE PAIN_48_HR_COMFORT_C = 'Yes'
) feh
CROSS APPLY (
SELECT Admit_Uncomfort_Yes = COUNT (PAIN_ADMIT_COMFORT_C)
FROM CASES
WHERE PAIN_ADMIT_COMFORT_C = 'Yes'
) auy

Your query, and the other answers, are very inefficient (multiple selects).
What you want is called a "pivot", and the most efficient way of coding it using just one select over the table (your query uses 4) is as follows:
select
sum(case when PAIN_48_HR_COMFORT_C = 'Yes' then 1 else 0 end) as Forty_Eight_Hours,
sum(case when PAIN_ADMIT_COMFORT_C = 'Yes' then 1 else 0 end) as Admit_Uncomfort_Yes
sum(case when PAIN_ADMIT_COMFORT_C = 'Yes' AND PAIN_48_HR_COMFORT_C = 'NO' then 1 else 0 end) as Improved_pain
FROM CASES
I'm not sure what the columns mean - you may need to change a 'YES' to 'NO' etc to get the "has"/"has not" pain correct.

Related

SQL Countifs function

Hello,
I reach a dead end. I have a table with piecemark_1, piecemark_2, and weld_type.
I wanted to have a final output as per image show.
In excel I would usually use the formula "COUNTIFS+COUNTIFS" but now my data is getting bigger.
I tried to UNION both piecemark together from column piecemark_1 and piecemark_2 to create another table but I do not know how to countifs from another table.
I was wondering how can I solve this issue.
You seems want :
select piecemark, SUM(CASE WHEN WELD_TYPE = 'SW' THEN 1 ELSE 0 END) AS SW,
SUM(CASE WHEN WELD_TYPE = 'FW' THEN 1 ELSE 0 END) AS FW, COUNT(*) AS Total
from t cross apply
( values (piecemark_1), (piecemark_2)
) tt(piecemark)
group by piecemark
having count(*) > 1;
For all piecemark you need to exclude having clause.

SQL Nested Select statements with COUNT()

I'll try to describe as best I can, but it's hard for me to wrap my whole head around this problem let alone describe it....
I am trying to select multiple results in one query to display the current status of a database. I have the first column as one type of record, and the second column as a sub-category of the first column. The subcategory is then linked to more records underneath that, distinguished by status, forming several more columns. I need to display every main-category/subcategory combination, and then the count of how many of each sub-status there are beneath that subcategory in the subsequent columns. I've got it so that I can display the unique combinations, but I'm not sure how to nest the select statements so that I can select the count of a completely different table from the main query. My problem lies in that to display the main category and sub category, I can pull from one table, but I need to count from a different table. Any ideas on the matter would be greatly appreciated
Here's what I have. The count statements would be replaced with the count of each status:
SELECT wave_num "WAVE NUMBER",
int_tasktype "INT / TaskType",
COUNT (1) total,
COUNT (1) "LOCKED/DISABLED",
COUNT (1) released,
COUNT (1) "PARTIALLY ASSEMBLED",
COUNT (1) assembled
FROM (SELECT DISTINCT
(t.invn_need_type || ' / ' || s.code_desc) int_tasktype,
t.task_genrtn_ref_nbr wave_num
FROM sys_code s, task_hdr t
WHERE t.task_genrtn_ref_nbr IN
(SELECT ship_wave_nbr
FROM ship_wave_parm
WHERE TRUNC (create_date_time) LIKE SYSDATE - 7)
AND s.code_type = '590'
AND s.rec_type = 'S'
AND s.code_id = t.task_type),
ship_wave_parm swp
GROUP BY wave_num, int_tasktype
ORDER BY wave_num
Image here: http://i.imgur.com/JX334.png
Guessing a bit,both regarding your problem and Oracle (which I've - unfortunately - never used), hopefully it will give you some ideas. Sorry for completely messing up the way you write SQL, SELECT ... FROM (SELECT ... WHERE ... IN (SELECT ...)) simply confuses me, so I have to restructure:
with tmp(int_tasktype, wave_num) as
(select distinct (t.invn_need_type || ' / ' || s.code_desc), t.task_genrtn_ref_nbr
from sys_code s
join task_hdr t
on s.code_id = t.task_type
where s.code_type = '590'
and s.rec_type = 'S'
and exists(select 1 from ship_wave_parm p
where t.task_genrtn_ref_nbr = p.ship_wave_nbr
and trunc(p.create_date_time) = sysdate - 7))
select t.wave_num "WAVE NUMBER", t.int_tasktype "INT / TaskType",
count(*) TOTAL,
sum(case when sst.sub_status = 'LOCKED' then 1 end) "LOCKED/DISABLED",
sum(case when sst.sub_status = 'RELEASED' then 1 end) RELEASED,
sum(case when sst.sub_status = 'PARTIAL' then 1 end) "PARTIALLY ASSEMBLED",
sum(case when sst.sub_status = 'ASSEMBLED' then 1 end) ASSEMBLED
from tmp t
join sub_status_table sst
on t.wave_num = sst.wave_num
group by t.wave_num, t.int_tasktype
order by t.wave_num
As you notice, I don't know anything about the table with the substatuses.
You can use inner join, grouping and count to get your result:
suppose tables are as follow :
cat (1)--->(n) subcat (1)----->(n) subcat_detail.
so the query would be :
select cat.title cat_title ,subcat.title subcat_title ,count(*) as cnt from
cat inner join sub_cat on cat.id=subcat.cat_id
inner join subcat_detail on subcat.ID=am.subcat_detail_id
group by cat.title,subcat.title
Generally when you need different counts, you need to use the CASE statment.
select count(*) as total
, case when field1 = "test' then 1 else 0 end as testcount
, case when field2 = 'yes' then 1 else 0 endas field2count
FROM table1

SQL 2 counts with different filter

I have a table and I need calculate two aggregate functions with different conditions in one statement. How can I do this?
Pseudocode below:
SELECT count(CoumntA) *< 0*, count(CoumntA) * > 0*
FROM dbo.TableA
This is the same idea as tombom's answer, but with SQL Server syntax:
SELECT
SUM(CASE WHEN CoumntA < 0 THEN 1 ELSE 0 END) AS LessThanZero,
SUM(CASE WHEN CoumntA > 0 THEN 1 ELSE 0 END) AS GreaterThanZero
FROM TableA
As #tombom demonstrated, this can be done as a single query. But it doesn't mean that it should be.
SELECT
SUM(CASE WHEN CoumntA < 0 THEN 1 ELSE 0 END) AS less_than_zero,
SUM(CASE WHEN CoumntA > 0 THEN 1 ELSE 0 END) AS greater_than_zero
FROM
TableA
The time when this is not so good is...
- There is an index on CoumntA
- Most values (50% or more feels about right) are exactly zero
In that case, two queries will be faster. This is because each query can use the index to quickly home in on the section to be counted. In the end only counting the relevant records.
The example I gave, however, scans the whole table every time. Only once, but always the whole table. This is worth it when you're counting most of the records. In your case it looks liek you're counting most or all of them, and so this is probably a good way of doing it.
It is possible to do this in one select statement.
The way I've done it before is like this:
SELECT SUM(CASE WHEN ColumnA < 0 THEN 1 END) AS LessThanZero,
SUM(CASE WHEN ColumnA > 0 THEN 1 END) AS GreaterThanZero
FROM dbo.TableA
This is the correct MS SQL syntax and I believe this is a very efficient way of doing it.
Don't forget you are not covering the case when ColumnA = 0!
select '< 0' as filter, COUNT(0) as cnt from TableA where [condition 1]
union
select '> 0' as filter, COUNT(0) as cnt from TableA where [condition 2]
Be sure that condition 1 and condition 2 create a partition on the original set of records, otherwise same records could be counted in both groups.
For SQL Server, one way would be;
SELECT COUNT(CASE WHEN CoumntA<0 THEN 1 ELSE NULL END),
COUNT(CASE WHEN CoumntA>0 THEN 1 ELSE NULL END)
FROM dbo.TableA
Demo here.
SELECT
SUM(IF(CoumntA < 0, 1, 0)) AS lowerThanZero,
SUM(IF(CoumntA > 0, 1, 0)) AS greaterThanZero
FROM
TableA
Is it clear what's happening? Ask, if you have any more questions.
A shorter form would be
SELECT
SUM(CoumntA < 0) AS lowerThanZero,
SUM(CoumntA > 0) AS greaterThanZero
FROM
TableA
This is possible, since in MySQL a true condition is equal 1, a false condition is equal 0
EDIT: okay, okay, sorry, don't know why I thought it's about MySQL here.
See the other answers about correct syntax.

Count the "ratio?" for wins and losses

I've already got this to work but it's a really bad approach and i need some help with factorization of my query.
SELECT `GameDate`,
COUNT(CASE
WHEN `P1Outcome`= 'win' AND P2Param = 'a' THEN 1
END) AS a_win,
COUNT(CASE
WHEN `P1Outcome`= 'loss' AND P2Param = 'a' THEN 1
END) AS a_loss,
COUNT(CASE
WHEN `P1Outcome`= 'win' AND P2Param = 'b' THEN 1
END) AS b_win,
COUNT(CASE
WHEN `P1Outcome`= 'loss' AND P2Param = 'b' THEN 1
END) AS b_loss
FROM games
WHERE `P2Param` IN ( 'a', 'b', 'c' )
GROUP BY GameDate
This will get me an query that i can use in my php application but i would like to skip having to make the actual ratio calculation in php and fetch it directly with SQL.
So basically what i've been trying to do is something similar to this:
COUNT(CASE
WHEN `P1Outcome`= 'win' AND P2Param = 'a' THEN 1 Else -1
END) AS a_ratio,
But just as the beginer i'm, i can't figure it out how i can make this to work.
EDIT:
Sorry for not explaining my regards in more details, here is the thing. I'm creating an statistics component and i need to fetch the ratio for a period of time in order to display it as an graph. So the following things are required:
GameDate (1,2,3,4... days ago)
The ratio for the player based on to different params (maps in this case)
So in short this is what i got at the moment:
GameDate a_win a_loss b_win b_loss
2011/04/25 x x x x
2011/04/23 x x x x
....
So everything works out great, but i would like to have the actual ratio calculation made in SQL because at the moment i need to make it in php e.g $ratio = $q[a_win]-$q[a_loss] and due to the fact that I've a lot of different param my query is like double the size because i need to fetch both the win and loss instead of just the ratio like i want in the first place.
SELECT `GameDate`,
SUM(CASE `P2Param`
WHEN 'a' THEN CASE P1Outcome WHEN 'win' THEN 1 ELSE -1 END
ELSE 0
END) AS a_ratio,
SUM(CASE `P2Param`
WHEN 'b' THEN CASE P1Outcome WHEN 'win' THEN 1 ELSE -1 END
ELSE 0
END) AS b_ratio
FROM games
WHERE `P2Param` IN ( 'a', 'b', 'c' )
GROUP BY GameDate
What is very important COUNT just returns number of rows regardless of what you count unless you use COUNT(DISTINCT ...) which in turn returns count of distinct values. To sum up some values which you produce for every row use SUM(...). Make sure what happens in your DBMS when an expression in the SUM() function evaluates to NULL - some databases will just make the whole SUM return NULL.
To get ratio of any kind of data you can do:
select _date_, (SUM(case when _your_test_ then 1 else 0 end) / count(1)) as ratio
from _yout_table_
group by _date_
And if you just want to calculate a sum of some values:
select _date_, SUM(case when _your_test_ then 1 else 0 end) as number_of_something
from _yout_table_
group by _date_
What about:
SELECT COUNT(id), P1Outcome, P2Param
FROM games
GROUP BY P2Param, P1Outcome
It will returns you a resultset with 3 columns:
counted values for param and outcome
outcome
param
With that you can do more in my opinion.
SELECT GameDate, P2Param, SUM(CASE WHEN P1Outcome = 'win' THEN 1 ELSE -1 END) AS ratio
FROM games
GROUP BY GameDate, P2Param

Most optimized way to get column totals in SQL Server 2005+

I am creating some reports for an application to be used by various states. The database has the potential to be very large. I would like to know which way is the best way to get column totals.
Currently I have SQL similar to the following:
SELECT count(case when prg.prefix_id = 1 then iss.id end) +
count(case when prg.prefix_id = 2 then iss.id end) as total,
count(case when prg.prefix_id = 1 then iss.id end) as c1,
count(case when prg.prefix_id = 2 then iss.id end) as c2
FROM dbo.TableName
WHERE ...
As you can see, the columns are in there twice. In one instance, im adding them and showing the total, in the other im just showing the individual values which is required for the report.
This is a very small sample of the SQL, there are 20+ columns and w/i those columns 4 or more of them are being summed at times.
I was thinking of declaring some #Parameters and setting each of the columns equal to a #Parameter, then I could just add up which ever #Parameters I needed to show the column totals, IE: SET #Total = #c1 + #c2
But, does the SQL Server engine even care the columns are in there multiple times like that? Is there a better way of doing this?
Any reason this isn't done as
select prg.prefix_id, count(1) from tablename where... group by prg.prefix_id
It would leave you with a result set of the prefix_id and the count of rows for each prefix_ID...might be preferential over a series of count(case) statements, and I think it should be quicker, but I can't confirm for sure.
I would use a subquery before resorting to #vars myself. Something like this:
select c1,c2,c1+c1 as total from
(SELECT
count(case when prg.prefix_id = 1 then iss.id end) as c1,
count(case when prg.prefix_id = 2 then iss.id end) as c2
FROM dbo.TableName
WHERE ... ) a
Use straight SQL if you can before resorting to T-SQL procedure logic. Rule of thumb if you can do it in SQL do it in SQL. If you want to emulate static values with straight SQL try a inline view like this:
SELECT iv1.c1 + iv1.c2 as total,
iv1.c1,
iv1.c2
FROM
(
SELECT count(case when prg.prefix_id = 1 then iss.id end) as c1,
count(case when prg.prefix_id = 2 then iss.id end) as c2
FROM dbo.TableName
WHERE ...
) AS iv1
This way you logically are getting the counts once and can compute values based on those counts. However I think SQL Server is smart enough to not have to scan for the count n number of times so I don't know that your plan would differ from the SQL I sent and the SQL you have.