Having problem with putting values into ranges - sql

I have the following statement
SELECT
CASE
WHEN response BETWEEN 0 AND 5 then '0 - 5'
WHEN response BETWEEN 6 AND 10 then '6 - 10'
else 'over 10'
end AS 'range',
COUNT(*) as 'count'
FROM((SELECT datediff(MIN(referral_interventions.dateOfintervention), referral.referralDate) as response
from referral
LEFT JOIN referral_interventions on referral.refid =referral_interventions.refID
LEFT JOIN interventions on referral_interventions.typeOfintervention = interventions.intid
WHERE referral.referralDate > '2021-11-01'
GROUP BY referral.refid)
AS tempdiff)
GROUP BY 'range';
The select statement within the first FROM works and produces 119 results with response ranging from 0 to 12.
The full statement produces all 119 showing as
range count
0 - 5 119
Any ideas wher my syntax etc might be wrong

Sorted
I changed ' to ` around range and count

Related

See the distribution of secondary requests grouped by time interval in sql

I have the following table:
RequestId,Type, Date, ParentRequestId
1 1 2020-10-15 null
2 2 2020-10-19 1
3 1 2020-10-20 null
4 2 2020-11-15 3
For this example I am interested in the request type 1 and 2, to make the example simpler. My task is to query a big database and to see the distribution of the secondary transaction based on the difference of dates with the parent one. So the result would look like:
Interval,Percentage
0-7 days,50 %
8-15 days,0 %
16-50 days, 50 %
So for the first line from teh expected result we have the request with the id 2 and for the third line from the expected result we have the request with the id 4 because the date difference fits in this interval.
How to achieve this?
I'm using sql server 2014.
We like to see your attempts, but by the looks of it, it seems like you're going to need to treat this table as 2 tables and do a basic GROUP BY, but make it fancy by grouping on a CASE statement.
WITH dateDiffs as (
/* perform our date calculations first, to get that out of the way */
SELECT
DATEDIFF(Day, parent.[Date], child.[Date]) as daysDiff,
1 as rowsFound
FROM (SELECT RequestID, [Date] FROM myTable WHERE Type = 1) parent
INNER JOIN (SELECT ParentRequestID, [Date] FROM myTable WHERE Type = 2) child
ON parent.requestID = child.parentRequestID
)
/* Now group and aggregate and enjoy your maths! */
SELECT
case when daysDiff between 0 and 7 then '0-7'
when daysDiff between 8 and 15 then '8-15'
when daysDiff between 16 and 50 THEN '16-50'
else '50+'
end as myInterval,
sum(rowsFound) as totalFound,
(select sum(rowsFound) from dateDiffs) as totalRows,
1.0 * sum(rowsFound) / (select sum(rowsFound) from dateDiffs) * 100.00 as percentFound
FROM dateDiffs
GROUP BY
case when daysDiff between 0 and 7 then '0-7'
when daysDiff between 8 and 15 then '8-15'
when daysDiff between 16 and 50 THEN '16-50'
else '50+'
end;
This seems like basically a join and group by query:
with dates as (
select 0 as lo, 7 as hi, '0-7 days' as grp union all
select 8 as lo, 15 as hi, '8-15 days' union all
select 16 as lo, 50 as hi, '16-50 days'
)
select d.grp,
count(*) as cnt,
count(*) * 1.0 / sum(count(*)) over () as raio
from dates left join
(t join
t tp
on tp.RequestId = t. ParentRequestId
)
on datediff(day, tp.date, t.date) between d.lo and d.hi
group by d.grp
order by d.lo;
The only trick is generating all the date groups, so you have rows with zero values.

SQL Server Query to Transform a poorly collected date into a usable date to analyse age

I have a date field called BIRTH_DAT and the date of births have been recorded like this:
YYYMMDD, where the second number of the year is missing.
So an example would be a date of birth of 4th March 1978 would appear in this field as: 1780304
The rule in this field is that if it begins with a '1' then the date is in the 1900s, if it begins with a '2' then the date is in the 2000s
So what I want to do is to create another column that shows the correctly written version of the date so I can calculate age from it.
e.g
Column 1 is called BIRTH_DAT and has values:
1801204, 1601228, 1980803 ...
Column 2 is a new column called PROPER_DOB and has values:
19801204, 19601228, 19980803 ...
How do I go about this?
Original answer - post was tagged MySQL:
Using CASE expression and operating on a string with left, substring and concat functions will get you derired result. Based on first character we're replacing:
1 with 19
2 with 20
and for any other case when first letter is not in (1,2) we're printing unsupported date format:
select
birth_dat,
case
when left(birth_dat,1) = '1' then concat('19', substring(birth_dat from 2))
when left(birth_dat,1) = '2' then concat('20', substring(birth_dat from 2))
else 'unsupported date format'
end AS proper_dob
from yourtable
As #Siyual and #JanDoggen suggested, the right format for your column should be DATE which you could achieve by converting the string using specified format with str_to_date function like that:
select
birth_dat,
case
when left(birth_dat,1) = '1' then str_to_date(concat('19', substring(birth_dat from 2)), '%Y%m%d')
when left(birth_dat,1) = '2' then str_to_date(concat('20', substring(birth_dat from 2)), '%Y%m%d')
else 'unsupported date format'
end AS proper_dob
from yourtable
Live example for both queries: SQL fiddle
It turns out that OP is using SQL Server, so here's the edited answer:
Use CAST / CONVERT to achieve the same thing since there is not str_to_date function in SQL Server.
Use STUFF for inserting string in the another string value
SELECT *,
YEAR(GETDATE())-YEAR(PROPER_DATE) AS Age,
CASE WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) < 19 THEN '0-19y' ELSE '20y+' END AS AGE_GROUP
FROM(
SELECT BIRTH_DAT,
CASE WHEN CHARINDEX('1',BIRTH_DAT) = 1 THEN STUFF(CAST(BIRTH_DAT AS VARCHAR(30)),2,0,'9')
WHEN CHARINDEX('2',BIRTH_DAT) = 1 THEN STUFF(CAST(BIRTH_DAT AS VARCHAR(30)),2,0,'0')
END AS PROPER_DATE
FROM my_table
)M
SELECT *,YEAR(GETDATE())-YEAR(PROPER_DATE) AS Age,
CASE
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) < 5 THEN '0-5Y'
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) BETWEEN 5 AND 9 THEN '5-9Y'
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) BETWEEN 10 AND 14 THEN '10-14Y'
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) BETWEEN 15 AND 19 THEN '15-19Y'
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) >= 20 THEN '20+Y'
ELSE 'NK'
END AS AGE_GROUP,
COUNT(*)
FROM(
SELECT BIRTH_DAT
,SEX
,CASE
WHEN CHARINDEX('1',BIRTH_DAT) = 1 THEN STUFF(CAST(BIRTH_DAT AS VARCHAR(10)),2,0,'9')
WHEN CHARINDEX('2',BIRTH_DAT) = 1 THEN STUFF(CAST(BIRTH_DAT AS VARCHAR(10)),2,0,'0')
END AS PROPER_DATE
FROM mytable
)M
GROUP BY (CASE
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) < 5 THEN '0-5Y'
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) BETWEEN 5 AND 9 THEN '5-9Y'
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) BETWEEN 10 AND 14 THEN '10-14Y'
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) BETWEEN 15 AND 19 THEN '15-19Y'
WHEN YEAR(GETDATE())-YEAR(PROPER_DATE) >= 20 THEN '20+Y'
ELSE 'NK'
END

Want specific years between date joined and now

I am trying to get the members of a company that qualify for 'EMERITUS' status.
To qualify, one must be a member for 35 years from the date joined 'JOIN_DATE' and must be >=65 years of age to qualify 'BIRTH_DATE'. I want to see >= 2015 under the 'EMERITUS' column. Does this query make sense?
SELECT
N.ID, N.FULL_NAME, N.MEMBER_TYPE,
N.JOIN_DATE,DA.BIRTH_DATE,
(SELECT CASE
WHEN DATEDIFF(YEAR,N.JOIN_DATE,GETDATE()) + 35 > DATEDIFF(YEAR,DA.BIRTH_DATE,GETDATE()) + 65
THEN CONVERT(VARCHAR(4),YEAR(N.JOIN_DATE) + 35)
WHEN DATEDIFF(YEAR,N.JOIN_DATE,GETDATE()) + 35 < DATEDIFF(YEAR,DA.BIRTH_DATE,GETDATE()) + 65
THEN CONVERT(VARCHAR(4),YEAR(DA.BIRTH_DATE) + 65)
ELSE NULL
END) AS 'EMERITUS'
Based upon the comments above it looks like you are on the right track.
Using the below SQL (with example in SQLFiddle listed) you should be able to get the year they will be EMERITUS and the number of years until EMERITUS.
select N_sub.*
,case when DATEDIFF(d,GETDATE(),N_sub.EMERITUS)/365.0 > 0
then DATEDIFF(d,GETDATE(),N_sub.EMERITUS)/365.0
else 0
end YEARS_UNTIL_EMERITUS
from(select N."ID"
,N.FULL_NAME
,N.MEMBER_TYPE
,N.JOIN_DATE
,N.BIRTH_DATE
, (select case
when DATEDIFF (d,N.JOIN_DATE, GETDATE ())/365 + 35 > DATEDIFF(d,N.BIRTH_DATE, GETDATE ())/365 + 65
then CONVERT(VARCHAR(10),DATEADD(year,35,N.BIRTH_DATE),110)
when DATEDIFF (d,N.JOIN_DATE, GETDATE ())/365 + 35 < DATEDIFF(d,N.BIRTH_DATE, GETDATE ())/365 + 65
then CONVERT(VARCHAR(10),DATEADD(year,65,N.BIRTH_DATE),110)
else null
end) AS 'EMERITUS'
from N
) N_sub
SQL Fiddle: http://sqlfiddle.com/#!6/e464cc/7
With this query it is a bit better than just raw comparing the years as it goes by # of days divided by 365. Logic could be added to account for leap years. The results will show the date they get Emeritus and the number of years until they would get it. 0 if == 0 or negative.

Need to get success ratio for clients

I had a really nice guy in FreeNode IRC steer me closer to the answer.
The query I am using now is:
SELECT st.staff_id
, ROUND (100.0 * ( sum (case when s.code in ('10401','10402','10403') then 1
else 0 end)/count(s.code)), 1) as successes
from notes n join services s on n.zrud_service=s.zzud_service
join staff st on n.zrud_staff = st.zzud_staff
WHERE s.code IN ( '10401','10402','10403','10405')
AND n.date_service BETWEEN (now() - '30 days'::interval)::timestamp AND now()
group by st.staff_id;
(I did try /count(*) as well as a few other ways)
It does not error, and shows the results as either 100.0 or 0
I ran a query on just the codes grouped by staff and get different results. One staff discharged 23 people in the past month, 8 being unsuccessful (10405) This gives a percentage of 34.7% success rate. But the query shows 0%.
This is baffling. Anyone have suggestions?
Original question
I need to be able to see what percentage of successful discharges there are in a 30 day period. Here is a query that shows the discharges by code. I understand that I can use a "division" method in postgresql, but I have only been able to use it with two separate columns. Can someone assist in showing me how to divide data within a column?
I need to do something like: 10401'+'10402'+'10403' / 10401'+'10402'+'10403'+'10405'
SELECT n.date_creation, g.name AS Group, s.staff_id, n.date_service, c.client_id,
c.name_lastfirst_cs AS Client, q.code
FROM notes n, clients c, groups g, staff s,services q
WHERE n.visibility_flag = 1 -- valid note
AND notes.date_service BETWEEN (now() - '30 days'::interval)::timestamp AND now();
AND c.zzud_client = n.zrud_client AND n.zrud_group = g.zzud_group
AND n.zrud_staff = s.zzud_staff
AND q.code IN ('10401','10402','10403','10405') -- 10405 is unsuccessful discharge
AND n.zrud_service = q.zzud_service AND n.zrud_staff = ? ORDER BY n.date_service
If I re-write the query as such:
SELECT g.name AS Group, s.staff_id, c.client_id,
c.name_lastfirst_cs AS Client, q.code
FROM notes n, clients c, groups g, staff s,services q
WHERE n.visibility_flag = 1 -- valid note
AND notes.date_service BETWEEN (now() - '30 days'::interval)::timestamp AND now();
AND c.zzud_client = n.zrud_client AND n.zrud_group = g.zzud_group
AND n.zrud_staff = s.zzud_staff
AND n.zrud_service = q.zzud_service AND n.zrud_staff = ? ORDER BY n.date_service
OR, Instead of all the +'s, could I use the "SUM" operator?
I changed the query to:
SELECT g.name AS Group, s.staff_id AS Staff
SUM(CASE WHEN q.code BETWEEN '10401' AND '10405' THEN 1 ELSE 0 END) / SUM(CASE WHEN q.code BETWEEN '10401' AND '10405' THEN 0 ELSE 1 END)
AS success_ratio FROM FROM notes n, clients c, groups g, staff s,services q
AND n.date_service BETWEEN (now() - '30 days'::interval)::timestamp AND now()
AND q.code IN ('10401','10402','10403','10405')
AND c.zzud_client = n.zrud_client AND n.zrud_group = g.zzud_group
AND n.zrud_staff = s.zzud_staff
AND n.zrud_service = q.zzud_service AND s.staff_id = 'BATTNEAL1026' ORDER BY n.date_service
GROUP BY s.staff_id
And get this error:
ERROR: syntax error at or near "SUM"
LINE 2: SUM(CASE WHEN q.code BETWEEN '10401' AND '10405' THEN 1 ELSE...
^
********** Error **********
ERROR: syntax error at or near "SUM"
SQL state: 42601
Character: 45
Your problem is the division with two bigint / integer numbers. Since your result is always < 0, this results in either 0 or 1. Here:
sum (case when s.code in ('10401','10402','10403') then 1 else 0 end)
/count(s.code)
Multiply by 100.0 first.
The fractional digit in 100.0 coerces the calculation to be done in numeric, which preserves the fractional part.
With some other modifications and formatting, it could look like this:
SELECT st.staff_id
,round((count(s.code IN ('10401','10402','10403') OR NULL) * 100.0)
/ count(*), 1) AS successes
FROM notes n
JOIN services s ON s.zzud_service = n.zrud_service
JOIN staff st ON st.zzud_staff = n.zrud_staff
WHERE s.code IN ('10401','10402','10403','10405')
AND n.date_service BETWEEN (now() - '30 days'::interval) AND now()
GROUP BY st.staff_id;

Count records with a criteria like "within days"

I have a table as below on sql.
OrderID Account OrderMethod OrderDate DispatchDate DispatchMethod
2145 qaz 14 20/3/2011 23/3/2011 2
4156 aby 12 15/6/2011 25/6/2011 1
I want to count all records that have reordered 'within 30 days' of dispatch date where Dispatch Method is '2' and OrderMethod is '12' and it has come from the same Account.
I want to ask if this all can be achieved with one query or do I need to create different tables and do it in stages as I think I wll have to do now? Please can someone help with a code/query?
Many thanks
T
Try the following, replacing [tablename] with the name of your table.
SELECT Count(OriginalOrders.OrderID) AS [Total_Orders]
FROM [tablename] AS OriginalOrders
INNER JOIN [tablename] AS Reorders
ON OriginalOrders.Account = Reorders.Account
AND OriginalOrders.OrderDate < Reorders.OrderDate
AND DATEDIFF(day, OriginalOrders.DispatchDate, Reorders.OrderDate) <= 30
AND Reorders.DispatchMethod = '2'
AND Reorders.OrderMethod = '12';
By using an inner join you'll be sure to only grab orders that meet all the criteria.
By linking the two tables (which are essentially the same table with itself using aliases) you make sure only orders under the same account are counted.
The results from the join are further filtered based on the criteria you mentioned requiring only orders that have been placed within 30 days of the dispatch date of a previous order.
Totally possible with one query, though my SQL is a little stale..
select count(*) from table
where DispatchMethod = 2
AND OrderMethod = 12
AND DATEDIFF(day, OrderDate, DispatchDate) <= 30;
(Untested, but it's something similar)
One query can do it.
SELECT COUNT(*)FROM myTable reOrder
INNER JOIN myTable originalOrder
ON reOrder.Account = originalOrder.Account
AND reOrder.OrderID <> originalOrder.OrderID
-- all re-orders that are within 30 days or the
-- original orders dispatch date
AND DATEDIFF(d, originalOrder.DispatchDate, reOrder.OrderDate) <= 30
WHERE reOrder.DispatchMethod = 2
AND reOrder.OrderMethod = 12
You need a self-join.
The query below assumes that a given account will have either 1 or 2 records in the table - 2 if they've reordered, else 1.
If 3 records exist for a given account, 2 orders + 1 reorder then this won't work - but we'd then need more information on how to distinguish between an order and a reorder.
SELECT COUNT(*) FROM myTable new, myTable prev
WHERE new.DispatchMethod = 2
AND new.OrderMethod = 12
AND DATEDIFF(day, prev.DispatchDate, new.OrderDate) <=30
AND prev.Account == new.Account
AND prev.OrderDate < new.OrderDate
Can we use GROUP BY in this case, such as the following?
SELECT COUNT(Account)
FROM myTable
WHERE DispatchMethod = 2 AND OrderMethod = 12
AND DATEDIFF(d, DispatchDate, OrderDate) <=30
GROUP BY Account
Will the above work or am I missing something here?