Oracle SQL - Count, per month, how many times a site appears in the results - sql

I'm not sure if I will explain this correctly so apologies in advance.
I'm looking to put together a report that shows the number of times a site (central_site.site_code & central_site.site_name) appears in a report and then total this up for each month with a grand total at the end. The date to summarize into month values is job.actual_start_date
What I'm looking for is something like:
Site Code Site Name April May June July August Total
1234 HIGH STREET 2 4 3 3 2 14
3093 4TH AVENUE 10 5 8 8 7 38
The code I have got so far to produce all the information that I would like summarizing in the format above is:
select
central_site.site_code,
central_site.site_name,
job.actual_start_date
from
central_site
inner join job on job.site_code = central_site.site_code
inner join job_type on job.job_type_key = job_type.job_type_key
inner join job_status_log on job.job_number = job_status_log.job_number
where
job_type.job_type_code = 'G012' and
job_status_log.status_code = '5200'
I just don't know the syntax / formulas to be able to total each site up per month and then provide a total for the year.

I think you want conditional aggregation:
select cs.site_code, cs.site_name,
sum(case when extract(month from ?.datecol) = 1 then 1 else 0 end) as jan,
sum(case when extract(month from ?.datecol) = 2 then 1 else 0 end) as feb,
. . .,
count(*) as year_total
from central_site cs join
job j
on j.site_code = cs.site_code join
job_type jt
on j.job_type_key = jt.job_type_key join
job_status_log jsl
on j.job_number = js.job_number
where jt.job_type_code = 'G012' and
jsl.status_code = '5200' and
?.datecol >= date '2018-01-01' and
?.datecol < date '2019-01-01'
group by cs.site_code, cs.site_name;
This is assuming that "number of times" is simply a count. Your question doesn't specify *what column is used to specify the date. So, that element needs to be filled in.

Related

SQL help to display no. of openings for all branches for each month of a year

Hi I need to generate a SSRS report to show how many centers got opened for each month in a calendar year under each branch. report will have 13 columns, first column being all the branches in each row and remaining 12 columns will have months of an year as header. I'm trying to get a result of each branch having no. of openings per month, so I can feed SSRS to display in tabular format. If a branch doesnt have any openings for any month, I need to display 0.
Branch table
=============
Branchid
Branchname
CenterOpen table
================
CenterOpenID
BranchID
CenterOpenDate
below is the SQL I had written
WITH months(MonthNumber) AS (
SELECT
1
UNION ALL
SELECT
MonthNumber + 1
FROM
months
WHERE
MonthNumber < 12
),
cteBranch(BranchID, BranchName, TargetOpenDate, Month, Count) as (
SELECT
B.BranchID,
B.BranchName,
CS.TargetOpenDate,
MONTH(CS.TargetOpenDate) as Month,
count(Month(CS.TargetOpenDate)) as Count
FROM
Branch B
left join goal.CenterOpenSchedule CS ON CS.BranchID = B.BranchID
GROUP BY
B.BranchID,
B.BranchName,
CS.TargetOpenDate,
MONTH(CS.TargetOpenDate)
)
select
*
from
months
cross join cteBranch
order by
BranchID asc,
MonthNumber asc
If I use cross join, months are repeating for each branch, how to resolve this? your help is highly appreciated.
Not sure which database you are on.
There are different ways to extract month/year from date.
Based on your example SQL, I'm going to use MONTH()
select branchName,
count(case when month(centerOpenDate) = 1 then branchId end) Jan_Count
...
...
count(case when month(centerOpenDate) = 12 then branchId end) Dec_Count
from Branch b
join CenterOpen co
on (b.BranchId = co.BranchId)
where year(centerOpenDate) = <your year filter>
group by branchName
This will take care of your below requirements:
" first column being all the branches in each row and remaining 12 columns will have months of an year as header."
and also -
"If a branch doesnt have any openings for any month, I need to display 0."
Your question is not explicit, but you seem to want a single year -- so you need to filter on the year.
The rest is basically conditional aggregation:
select b.branchName,
sum(case when month(co.centerOpenDate) = 1 then 1 else 0 end) as jan,
sum(case when month(co.centerOpenDate) = 2 then 1 else 0 end) as feb,
. . .
sum(case when month(co.centerOpenDate) = 12 then 1 else 0 end) as dec
from Branch b join
CenterOpen co
on b.BranchId = co.BranchId
where year(co.centerOpenDate) = #year
group by b.branchName

SQL: How to get date range counts as rows instead of columns?

The generalized use case I have is to get record counts for a number of date ranges across one or more tables.
My specific use case is this:
For a patient encounter table (enc) and a pregnancy table (preg), get the counts of patients seen 9 months before the expected due date, 12 months before, 15 months before, etc.
I can get the data I need by doing an outer join on the encounter table with a where clause that boxes the time constraints. However, this seems to be inefficient, a lot of typing, and the data are not in the form I'd like (I would like each time window to be a row instead of a column).
Below is the query I currently have. How can I rewrite it to get the data row wise instead of column wise?
select
preg.org,
count(distinct nine.patient_id) `Pre-Delivery Visits (09 Months)`,
count(distinct twelve.patient_id) `Pre-Delivery Visits (12 Months)`,
count(distinct all.patient_id) `Pre-Delivery Visits (All)`,
count(distinct preg.patient_id) `All Pregnancies`
from
pregnancy preg
left outer join enc nine on preg.patient_id = nine.patient_id and nine.encounter_date < preg.est_delivery_date and nine.encounter_date > date_add(preg.est_delivery_date, (-30*9))
left outer join enc twelve on preg.patient_id = twelve.patient_id and twelve.encounter_date < preg.est_delivery_date and twelve.encounter_date > date_add(preg.est_delivery_date, (-30*12))
left outer join enc all on preg.patient_id = all.patient_id and all.encounter_date < preg.est_delivery_date
group by 1
;
Data are returned in this format:
org (09 Months) (12 months) (All) (All Pregnancies)
org x 1 10 15 20
org y 2 22 23 24
org z 200 202 230 250
I'd like to get the data like this
org time_box count
org x 09 mon 1
org y 09 mon 2
org z 09 mon 202
org x 12 mon 10
...
etc.
I'm not sure if this does what you want. This calculates non-overlapping groups, so 12 months is really 9-12 months:
select (case when e.encounter_date > p.est_delivery_date - 30*9 day
then 'nine'
when e.encounter_date > p.est_delivery_date - 30*12 day
then 'twelve'
when e.encounter_date is not null
then 'all pre-delivery'
else 'all pregnancy'
end) as grp,
count(distinct p.patient_id)
from pregnancy p left join
enc e
on e.patient_id = p.patient_id and
e.encounter_date < p.est_delivery_date
group by grp;

YTD for the below query

I want to add add the Year to date component to this code. I have tried some other ways but I am not getting what I would like to see. Can someone please help me revised this to include the YTD in addition to the Month to date that is already there?
SELECT
COST__DESC,
ST.AD_SRV_MTN AS MONTH_OF_AD,
COUNT(DISTINCT CM.CM_NBR) AS CMS,
MEM_MO AS MBR_MTH,
CMS/MBR_MTH*1000 AS CMS_PER_1000
FROM XTR.FT_CM AS CM
JOIN XTR.FT_ST AS ST ON ST.CM_NBR = CM.CM_NBR
JOIN XTR.DIM_MED_CST AS MC ON ST.CST_CK = MCC.CST_CK
JOIN XTR.DIM_AF AS AFF ON ST.PRO_CK = AFF.AFF_CK
JOIN XTR.DIM_ADJDCTN_STAT AS A_S ON ST.ADJDCTN_STAT_CK = A_S.ADJDCTN_STAT_CK
JOIN XTR.DIM_ADJ_OT AS OT ON ST.ADJ_CK = OT.ADJ_CK
LEFT JOIN
(SELECT
CALENDAR_YEAR_MONTH as YEAR_MO,
SUM(MBR.COUNT_NBR) as MEM_MO
FROM XTR.FT_MBR_MONTHS MBR
INNER JOIN DIM_MBR_C ON MBR.DB_MBR_CK = DIM_MBR_C.DB_MBR_CK
AND MBR.DATE_CK BETWEEN DIM_MBR_C.DB_eff_date_ck
AND DIM_MBR_C.DB_END_DATE_CK
INNER JOIN DIM_DATE DT ON ELI_DATE_CK = DT.DATE_CK
WHERE MBR.F_C_CK = 500058321 AND YEAR_MO >= 201701
GROUP BY 1) MM ON ST.AD_SRV_MTN = MM.YEAR_MO
WHERE ST.F_C_CK = 500058321 AND ST.ST_START_DATE_CK >= 20200101
AND ST.AD_SRV_MTN > 201912 AND MC.MED_DESC IN ('Er', 'IP')
AND ST.AD_SRV_MTN < ((EXTRACT (YEAR FROM CURRENT_DATE) *100) +
EXTRACT (MONTH FROM CURRENT_DATE))
GROUP BY 1,2,4
ORDER BY 1,2
Honestly I don't really get your SQL and what is counted, but: Your can play with dates quite easy in Teradata, as Dates are stored (and can be used) internally as INTEGER. Just keep in mind year 1900 as year 0 and format YYYYMMDD.
So e.g. 16-Apr-2020 is in Format YYYYMMDD 20200416 and if you take 1900 as 0 you'll end up with 1200416 which is the internal format. Just try SELECT CURRENT_DATE (INT); - So if you want compare YearNumers you just have to divide by 10000.
With this your can implement YTD as SUM (CASE WHEN CURRENT_DATE/10000 = <YourDateField>/10000 THEN <YourKPI> else 0 END) as YourKPI_YTD. Counting can be done by SUM...THEN 1 ELSE 0 END....

Bring through a newly created calculated column in another query

I have 2 separate queries below which run correctly.Now I've created a calculated column to provide a count of working days by YMs and would like to bring this through to query1(the join would be query1.Period = query2.Yms)
please see the query and outputs below.
SELECT Client, ClientGroup, Type, Value, Period, PeriodName, PeriodNumber, ClientName
FROM metrics.dbo.vw_KPI_001_Invoice
select YMs,sum(case when IsWorkDay = 'X' then 1 else 0 end) from IESAONLINE.Dbo.DS_Dates
where Year > '2013'
group by YMs
Query 1
Client ClientGroup Type Value Period PeriodName PeriodNumber ClientName
0LG0 KarroFoods Stock 5691.68 201506 Week 06 2015 35 Karro Foods Scunthorpe
Query 2
YMs (No column name)
201401 23
Would the following work:
SELECT Client, ClientGroup, Type, Value, Period, PeriodName, PeriodNumber, ClientName, cnt
FROM metrics.dbo.vw_KPI_001_Invoice q1
INNER JOIN (select YMs,sum(case when IsWorkDay = 'X' then 1 else 0 end) as cnt from IESAONLINE.Dbo.DS_Dates
where Year > '2013'
group by YMs ) q2 ON q1.Period = q2.YMs
If a value isn't always available then you might consider changing the INNER JOIN to an OUTER JOIN.

SQL Query: Calculating the deltas in a time series

For a development aid project I am helping a small town in Nicaragua improving their water-network-administration.
There are about 150 households and every month a person checks the meter and charges the houshold according to the consumed water (reading from this month minus reading from last month). Today all is done on paper and I would like to digitalize the administration to avoid calculation-errors.
I have an MS Access Table in mind - e.g.:
*HousholdID* *Date* *Meter*
0 1/1/2013 100
1 1/1/2013 130
0 1/2/2013 120
1 1/2/2013 140
...
From this data I would like to create a query that calculates the consumed water (the meter-difference of one household between two months)
*HouseholdID* *Date* *Consumption*
0 1/2/2013 20
1 1/2/2013 10
...
Please, how would I approach this problem?
This query returns every date with previous date, even if there are missing months:
SELECT TabPrev.*, Tab.Meter as PrevMeter, TabPrev.Meter-Tab.Meter as Diff
FROM (
SELECT
Tab.HousholdID,
Tab.Data,
Max(Tab_1.Data) AS PrevData,
Tab.Meter
FROM
Tab INNER JOIN Tab AS Tab_1 ON Tab.HousholdID = Tab_1.HousholdID
AND Tab.Data > Tab_1.Data
GROUP BY Tab.HousholdID, Tab.Data, Tab.Meter) As TabPrev
INNER JOIN Tab
ON TabPrev.HousholdID = Tab.HousholdID
AND TabPrev.PrevData=Tab.Data
Here's the result:
HousholdID Data PrevData Meter PrevMeter Diff
----------------------------------------------------------
0 01/02/2013 01/01/2013 120 100 20
1 01/02/2013 01/01/2012 140 130 10
The query above will return every delta, for every households, for every month (or for every interval). If you are just interested in the last delta, you could use this query:
SELECT
MaxTab.*,
TabCurr.Meter as CurrMeter,
TabPrev.Meter as PrevMeter,
TabCurr.Meter-TabPrev.Meter as Diff
FROM ((
SELECT
Tab.HousholdID,
Max(Tab.Data) AS CurrData,
Max(Tab_1.Data) AS PrevData
FROM
Tab INNER JOIN Tab AS Tab_1
ON Tab.HousholdID = Tab_1.HousholdID
AND Tab.Data > Tab_1.Data
GROUP BY Tab.HousholdID) As MaxTab
INNER JOIN Tab TabPrev
ON TabPrev.HousholdID = MaxTab.HousholdID
AND TabPrev.Data=MaxTab.PrevData)
INNER JOIN Tab TabCurr
ON TabCurr.HousholdID = MaxTab.HousholdID
AND TabCurr.Data=MaxTab.CurrData
and (depending on what you are after) you could only filter current month:
WHERE
DateSerial(Year(CurrData), Month(CurrData), 1)=
DateSerial(Year(DATE()), Month(DATE()), 1)
this way if you miss a check for a particular household, it won't show.
Or you might be interested in showing last month present in the table (which can be different than current month):
WHERE
DateSerial(Year(CurrData), Month(CurrData), 1)=
(SELECT MAX(DateSerial(Year(Data), Month(Data), 1))
FROM Tab)
(here I am taking in consideration the fact that checks might be on different days)
I think the best approach is to use a correlated subquery to get the previous date and join back to the original table. This ensures that you get the previous record, even if there is more or less than a 1 month lag.
So the right query looks like:
select t.*, tprev.date, tprev.meter
from (select t.*,
(select top 1 date from t t2 where t2.date < t.date order by date desc
) prevDate
from t
) join
t tprev
on tprev.date = t.prevdate
In an environment such as the one you describe, it is very important not to make assumptions about the frequency of reading the meter. Although they may be read on average once per month, there will always be exceptions.
Testing with the following data:
HousholdID Date Meter
0 01/12/2012 100
1 01/12/2012 130
0 01/01/2013 120
1 01/01/2013 140
0 01/02/2013 120
1 01/02/2013 140
The following query:
SELECT a.housholdid,
a.date,
b.date,
a.meter,
b.meter,
a.meter - b.meter AS Consumption
FROM (SELECT *
FROM water
WHERE Month([date]) = Month(Date())
AND Year([date])=year(Date())) a
LEFT JOIN (SELECT *
FROM water
WHERE DateSerial(Year([date]),Month([date]),Day([date]))
=DateSerial(Year(Date()),Month(Date())-1,Day([date])) ) b
ON a.housholdid = b.housholdid
The above query selects the records for this month Month([date]) = Month(Date()) and compares them to records for last month ([date]) = Month(Date()) - 1)
Please do not use Date as a field name.
Returns the following result.
housholdid a.date b.date a.meter b.meter Consumption
0 01/02/2013 01/01/2013 120 100 20
1 01/02/2013 01/01/2013 140 130 10
Try
select t.householdID
, max(s.theDate) as billingMonth
, max(s.meter)-max(t.meter) as waterUsed
from myTbl t join (
select householdID, max(theDate) as theDate, max(meter) as meter
from myTbl
group by householdID ) s
on t.householdID = s.householdID and t.theDate <> s.theDate
group by t.householdID
This works in SQL not sure about access
You can use the LAG() function in certain SQL dialects. I found this to be much faster and easier to read than joins.
Source: http://blog.jooq.org/2015/05/12/use-this-neat-window-function-trick-to-calculate-time-differences-in-a-time-series/