Convert answer to percentage with two decimals place SQL - percentage

I’m trying to figure out how to convert the Male Percentage column to a percentage with decimals to the hundredths
select Top 20 pa.State,
Sum(case when p.gender='M' then 1 else 0 end) as [Male Count],
Sum(case when p.gender='F' then 1 else 0 end) as [ Female Count],
100*sum(case when gender='m' then 1 else 0 end )/count(*) as [Male Percentage]
From [dbo].[Patients] as p
Join PatientAddresses as pa
on p.mrn=pa.MRN
group by pa.State
The results I got.
State
Male Count
Female Count
Male Percentage
UT
105
120
46
NC
1152
1123
50
WI
700
669
51
MA
1486
1424
51

SQL Server by default will report integer division as integer numbers. If you want to force two decimal places, use ROUND(x, 2), where x is a float. One way to make x a float here is to multiply the percentage by the float 100.0 instead of the integer 100.
SELECT TOP 20
pa.State,
COUNT(CASE WHEN p.gender = 'M' THEN 1 END) AS [Male Count],
COUNT(CASE WHEN p.gender = 'F' THEN 1 END) AS [Female Count],
ROUND(100.0*COUNT(CASE WHEN gender = 'm' THEN 1 END) / COUNT(*), 2) AS [Male Percentage]
FROM [dbo].[Patients] AS p
INNER JOIN PatientAddresses AS pa
ON p.mrn = pa.MRN
GROUP BY
pa.State;
Side note: Using TOP without ORDER BY does not make much sense, because it is not clear which 20 records you want to see. So, adding an ORDER BY clause here is probably what you want, unless you are OK with getting back 20 random states.
Edit:
If you want to view the output in SSMS with only two decimal places, and not just with a precision of 2 decimal places, then use CONVERT:
CONVERT(DECIMAL(10,2), 100.0*COUNT(CASE WHEN gender = 'm' THEN 1 END) / COUNT(*))

Related

Display hourly based data for 24 hour in SQL Server

I want to display the hourly based report for the last 24 hour. I have tried but the problem is that it will display count only where particular hour contains data.
But I want to display count for an hour and if count not found then display 0 over there.
select
datepart(hour, upload_date) as [hour], count(*)
from
tbl_stories
where
upload_date > getdate() - 1
group by
datepart(hour, upload_date)
Output:
hour count
-------------
11 2
16 1
17 1
but I want to get a record in the following way.
hour count
-------------
1 0
2 0
3 5
.
.
.
.
24 1
You can use a value() clause to generate all the hours and then use left join:
select v.hh, count(s.upload_date)
from (values (0), (1), . . . (23)
) v(hh) left join
tbl_stories s
on datepart(hour, s.upload_date) = v.hh and
s.upload_date > getdate() - 1
group by v.hh
order by v.hh;
Note that hours go from 0 to 23.
If you don't want to list out the hours, a convenient generation method is a recursive CTE:
with hours as (
select 1 as hh
union all
select hh + 1
from hours
where hh < 23
)
select h.hh, count(s.upload_date)
from hours h
tbl_stories s
on datepart(hour, s.upload_date) = h.hh and
s.upload_date > getdate() - 1
group by h.hh
order by h.hh;

Retain values only for certain section of data in teradata

Below is the link of my previous quetsion.
Retain values till there is a change in value in Teradata
It worked as suggested by one of the community members #Dnoeth. Can this retention be done only for certain section of data?
I.e, Retain data only for data where Dep is A or B . When Dep is C just use same value as input and no need to retain till certain value.
Data:
Cust_id Balance st_ts Dep
123 1000 27MAY2018 A
123 350 31MAY2018 A
256 2000 29MAY2018 B
345 1000 28APR2018 C
345 1200 26MAY2018 C
Output reqd:
Cust_id Balance st_ts Dep
123 1000 27MAY2018 A
123 1000 28MAY2018 A
123 1000 29MAY2018 A
123 1000 30MAY2018 A
123 350 31MAY2018 A
256 2000 29MAY2018 B
256 2000 30MAY2018 B
256 2000 31MAY2018 B
345 1000 28APR2018 C
345 1200 26MAY2018 C
Query used:
Wth cte
{
SELECT customer_id, bal, st_ts,
-- return the next row's date
Coalesce(Min(st_ts)
Over (PARTITION BY customer_id
ORDER BY st_ts
ROWS BETWEEN 1 Following AND 1 Following)
,Date '2018-06-01') AS next_Txn_dt
FROM BAL_DET;
}
SELECT customer_id, bal
,Last(pd) -- last day of the period
FROM cTE
-- make a period of the current and next row's date
-- and return one row per day
EXPAND ON PERIOD(ST_TS, next_Txn_dt) AS pd;
Thanks
Sandy
You can add a CASE to check for Dep = 'C':
WITH cte AS
( SELECT customer_id, bal, st_ts, dep,
-- return the next row's date
CASE
WHEN dep = 'C'
THEN st_ts +1 -- simply increase date
ELSE
Coalesce(Min(st_ts)
Over (PARTITION BY customer_id
ORDER BY st_ts
ROWS BETWEEN 1 Following AND 1 Following)
,DATE '2018-06-01')
END AS next_Txn_dt
FROM BAL_DET
)
SELECT customer_id, bal
,Last(pd) -- last day of the period
,dep
FROM cTE
-- make a period of the current and next row's date
-- and return one row per day
EXPAND ON PERIOD(ST_TS, next_Txn_dt) AS pd

Sqlite how to select case for multiple values in a column

So, I have this table:
id|otherid|key|value
--------------------
1 1 ak av
2 1 bk bv
3 2 ak av
3 2 ak av2
The things to note is that other ids are repeating and they can have same keys with values multiple times. The thing I want to retrieve would be the value for the key, or, if there are multiple values for same key some string.
So, I'd like to receive for otherids
otherid|key|value
-----------------
1 ak av
1 bk bv
2 ak SEQUENCE
Where 'SEQUENCE' string allows me to know that there are multiple values for the single key for otherid. What query would accomplish this?
To get one output row for multiple input rows, use grouping.
The count of rows in the group is available with COUNT(*); you can handle the cases with a CASE expression:
SELECT otherid,
key,
CASE COUNT(*)
WHEN 1 THEN MIN(value)
ELSE 'SEQUENCE'
END AS value
FROM MyTable
GROUP BY otherid,
key;
SELECT DISTINCT
otherid,key,
(SELECT
CASE
WHEN COUNT(value)=1 THEN value
WHEN COUNT(value)=0 THEN '*nil*'
ELSE '*sequence*'
END)
FROM datasingle
WHERE otherid=myid GROUP BY key;

Comparing values from 2 case statements

I have the oracle 11g version installed and am practicing on the HR test database package. This question is a much smaller version of what I require:
here is my query:
Select description, Case_First_Level, Case_Second_Level, Case_Difference
from
(Select p.country_name as description,
(case when (l.location_id between '1000' and '1300') then (l.postal_code) else '0' end) as Case_First_Level,
(case when (l.location_id between '1400' and '1700') then (l.postal_code) else '0' end) as Case_Second_Level,
(case when
(
(case when l.location_id between '1000' and '1300' then (l.postal_code) else '0' end) >
(case when l.location_id between '1400' and '1700' then (l.postal_code) else '0' end)
)
then 1000 else 2000 end) as Case_Difference
from countries p, locations l
where l.country_id = p.country_id
and p.country_id in ('IT', 'US', 'CN')
group by p.country_name, p.country_id, l.country_id, l.postal_code, l.location_id
)
This query works fine, and I get the following output:
DESCRIPTION CASE_FIRST_LEVEL CASE_SECOND_LEVEL CASE_DIFFERENCE
United States of America 0 26192 2000
China 0 0 2000
Italy 10934 0 1000
United States of America 0 50090 2000
Italy 00989 0 1000
United States of America 0 99236 2000
United States of America 0 98199 2000
Now, I want to replace the values 1000 and 2000 in the Case_Difference column by the values from the conditions, i.e instead of having the static values, I want to replace the value 1000 by the values from (Case_First_Level - Case_Second_Level) and vice versa for the value 2000. Now, since aliases are not recognised here, is there any way to do this?
Thanks
EDIT: Uploaded correct code now.
Try:
SELECT DESCRIPTION,
CASE_FIRST_LEVEL,
CASE_SECOND_LEVEL,
ABS(CASE_FIRST_LEVEL - CASE_SECOND_LEVEL) AS CASE_DIFFERENCE
FROM (Select p.country_name as description,
case
when l.location_id between '1000' and '1300'
then l.postal_code
else '0'
end as Case_First_Level,
case
when l.location_id between '1400' and '1700'
then l.postal_code
else '0'
end as Case_Second_Level,
from countries p
INNER JOIN locations l
ON l.country_id = p.country_id
WHERE p.country_id in ('IT', 'US', 'CN')
group by p.country_name,
p.country_id,
l.country_id,
l.postal_code,
l.location_id)
Note that the calculation ABS(CASE_FIRST_LEVEL - CASE_SECOND_LEVEL) AS CASE_DIFFERENCE counts on CASE_FIRST_LEVEL and CASE_SECOND_LEVEL being convertible to NUMBER which may not always be the case as they're postal code values (consider, for example, the British postal codes which look something like 'W11 2BQ').
Share and enjoy.

generate 6 numbers between 1 and 2 in a 2:1 ratio in SQL or PL/SQL

how can i generate 6 numbers between 1 and 2 where 4 of the numbers will be 1 and the other 2 will be 2 in a random order i.e.
results
1
2
1
1
1
2
and also in a different ratio i.e. 3:2:1 for numbers between 1 and 3 for 12 numbers
i.e.
results
1
1
2
3
1
2
1
3
1
1
3
3
results don't have to be in this order but in the ratios as above in oracle SQL or PL/SQL
To get the ratios perfect you could do something like this - generate all the numbers, then sort in random order:
SELECT r
FROM (SELECT CASE
WHEN ROWNUM <=4 THEN 1
ELSE 2
END AS r
FROM DUAL
CONNECT BY LEVEL <= 6)
ORDER BY DBMS_RANDOM.value;
R
----------------------
2
1
1
2
1
1
I think this will work in straight SQL; it's horrifically inefficient, and a PL/SQL one might be less so. It's also completely static; differing ratios call for a different number of values selected.
select value
from (
select mod(value, 2) + 1 as value,
row_number() over (partition by
case mod(value, 2) = 1
then 1
else 0
end) as twos_row,
row_number() over (partition by
case mod(value, 2) = 0
then 1
else 0
end) as ones_row
from (select dbms_crypto.randominteger as value
from dba_objects
order by object_id
)
)
where twos_rows <= 2
or ones_rows <= 4
The inner-most select grabs a big stack of random numbers. The next query out determines whether that random value would be a 2 or a 1 by mod'ing the earlier random value. The last level of nesting just filters out all the rows after the correct number of that type of row has been returned.
This is untested and fragile. If you need a solution that's reliable and performance, I'd recommend PL/SQL, where you
loop
pick off random numbers
determine what partition in your set of values they'd fit into
keep them if that partition hasn't been satisfied
exit when all partitions have been satisfied.

Resources