Sum of numbers in CTE - sql

For this CTE show counts of working/not working days.
with a(id, MON, TUE, WED, THUR, FRI, SAT, SUN) as (values (1,0,0,1,1,1,0,0),(2,1,1,1,1,0,0,0))
select * from a
I got this result but I changed CTE.
My request:
with a(id, days) as (values (1,0),(1,0),(1,1),(1,1),(1,1),(1,0),(1,0),(2,1),(2,1),(2,1),(2,1),(2,0),(2,0),(2,0))
select id, 'Working' as day_type, sum(days) "COUNT" from a group by shop_id union select id, 'Non-working' as day_type, count(days) - sum(days) "COUNT" from a group by id order by id, day_type

If I understand your question, you want to be able to generate the output from your second query without altering the initial CTE a (which presumably represents an actual table or something).
There will be multiple ways of doing this, but one possibility is something like:
with a(shop_id, MON, TUE, WED, THUR, FRI, SAT, SUN) as (values (1,0,0,1,1,1,0,0),(2,1,1,1,1,0,0,0))
, b as (SELECT shop_id, MON + TUE + WED + THUR + FRI + SAT + SUN cnt FROM a)
SELECT shop_id, 'Working', cnt
FROM b
GROUP BY shop_id, cnt
UNION ALL
SELECT shop_id, 'Non-working', 7-cnt
FROM b
GROUP BY shop_id, cnt
Here, the CTE b contains the sum of working days for each shop_id. This is then unioned together with the non-working days (7-working). Using the second CTE isn't necessary, but lets us avoid repeating the bit that adds all the days together. If you prefer, you could do:
with a(shop_id, MON, TUE, WED, THUR, FRI, SAT, SUN) as (values (1,0,0,1,1,1,0,0),(2,1,1,1,1,0,0,0))
SELECT shop_id, 'Working', MON + TUE + WED + THUR + FRI + SAT + SUN cnt
FROM a
UNION ALL
SELECT shop_id, 'Non-working', 7-(MON + TUE + WED + THUR + FRI + SAT + SUN)
FROM a
Another options is to actually unpivot the table (convert the columns into rows) and then group by the shop_id and day_type. Something like:
with a(shop_id, MON, TUE, WED, THUR, FRI, SAT, SUN) as (values (1,0,0,1,1,1,0,0),(2,1,1,1,1,0,0,0))
,
b AS (SELECT shop_id, CASE P.w WHEN 0 THEN 'Non-working' ELSE 'Working' END day_type
FROM a, TABLE (VALUES(a.MON),
(a.TUE),
(a.WED),
(a.THUR),
(a.FRI),
(a.SAT),
(a.SUN)) AS P(w))
SELECT shop_id, day_type, count(*) cnt
FROM b
GROUP BY shop_id, day_type
In all cases above, you can use an ORDER BY to get the rows in the order you want them.
Here is a Fiddle of these working.

Related

sql sum by month and year

I have tried by writing a correlated query but fails to result in output....I am trying using only joins now
SELECT sum(t1.acc)
,t1.mon
,t1.yr
FROM gl t1
WHERE mon IN (
SELECT mon
FROM gl t2
WHERE t2.mon <= t1.mon
AND t2.yr = t1.yr
)
GROUP BY t1.mon
,t1.yr
You should join your data with itself and apply the sum condition.
drop table if exists #input_data
select * into #input_data
from
(
select 1 as acc, 5 as mon, 2020 as yr union all
select 2 as acc, 6 as mon, 2020 as yr union all
select 3 as acc, 4 as mon, 2021 as yr union all
select 4 as acc, 3 as mon, 2021 as yr
) d
select
sum(e0.acc) as acc_sum,
d.mon,
d.yr
from
#input_data d
join #input_data e0
on e0.yr = d.yr and e0.mon <= d.mon
group by
d.yr, d.mon
order by
d.yr, d.mon
If you have multiple records for same mon/yr then you have to add additional grouping.
drop table if exists #input_data
select * into #input_data
from
(
select 1 as acc, 5 as mon, 2020 as yr union all
select 2 as acc, 6 as mon, 2020 as yr union all
select 3 as acc, 4 as mon, 2021 as yr union all
select 4 as acc, 3 as mon, 2021 as yr union all
select 0 as acc, 5 as mon, 2020 as yr
) d
;with cte_source as
(
select
sum(acc) as acc,
mon,
yr
from #input_data d
group by d.yr, d.mon
)
select
sum(e0.acc) as acc_sum,
d.mon,
d.yr
from
cte_source d
join cte_source e0
on e0.yr = d.yr and e0.mon <= d.mon
group by
d.yr, d.mon
order by
d.yr, d.mon
You seem to want a cumulative sum within a year:
select year, mon, sum(acc) over (partition by year order by mon)
from t
order by year, acc;
Getting the data back in the original order is a bit tricky but just requires an explicit order by.

Select the highest order delivered

Following is my output:
MONTH STAF STAFFNAME TOTAL_ORDER_DELIVERED
===== ==== ==================== =====================
JAN S009 Theresina Ertelt 1
FEB S015 Lonna Charker 1
MAR S003 Suzi Maccari 2
MAR S010 Zacharie Witty 1
MAR S020 Abbie Gosnoll 1
MAR S017 Renee Alston 1
AUG S006 Falito Ollerton 1
AUG S017 Renee Alston 1
AUG S003 Suzi Maccari 1
OCT S003 Suzi Maccari 3
OCT S020 Abbie Gosnoll 2
What I want is:
MONTH STAF STAFFNAME TOTAL_ORDER_DELIVERED
===== ==== ==================== =====================
JAN S009 Theresina Ertelt 1
FEB S015 Lonna Charker 1
MAR S003 Suzi Maccari 2
AUG S006 Falito Ollerton 1
AUG S017 Renee Alston 1
AUG S003 Suzi Maccari 1
OCT S003 Suzi Maccari 3
I want to select the highest result based on the month but can't figure what to do. Here are my query in SQL:
SELECT TO_CHAR(TO_DATE(EXTRACT(MONTH FROM receivedDate),'mm'),'MON') AS Month,
d.staffID, staffname, count(deliveryID) AS Total_Order_Delivered
FROM delivery d, deliverystaff s
WHERE (d.staffid = s.staffid)
AND (EXTRACT(YEAR FROM receivedDate) = 2020)
GROUP BY EXTRACT(MONTH FROM d.receivedDate),d.staffid, staffname
ORDER BY EXTRACT(MONTH FROM d.receivedDate),count(deliveryID) desc;
I would suggest using RANK here:
WITH cte AS (
SELECT TO_CHAR(TO_DATE(EXTRACT(MONTH FROM receivedDate), 'mm'), 'MON') AS Month,
EXTRACT(MONTH FROM d.receivedDate) AS month_num,
d.staffID, staffname, COUNT(deliveryID) AS Total_Order_Delivered,
RANK() OVER (PARTITION BY EXTRACT(MONTH FROM d.receivedDate), d.staffid, staffname
ORDER BY COUNT(deliveryID) DESC) rnk
FROM delivery d
INNER JOIN deliverystaff s ON d.staffid = s.staffid
WHERE EXTRACT(YEAR FROM receivedDate) = 2020
GROUP BY EXTRACT(MONTH FROM d.receivedDate), d.staffid, staffname
)
SELECT Month, staffID, staffname, Total_Order_Delivered
FROM cte
WHERE rnk = 1
ORDER BY month_num;
Tim's answer is fine. However, I strongly encourage you to make some changes to the query.
First, for the where clause don't use extract(). Use direct date comparisons. Second, include the year and month in the aggregation. Then, be sure that you qualify all column references.
That allows you to do:
SELECT sd.*
FROM (SELECT TO_CHAR(d.receivedDate, 'YYYY-MON') AS Month_year,
s.staffID, s.staffname, COUNT(*) AS Total_Order_Delivered,
RANK() OVER (PARTITION BY TO_CHAR(d.receivedDate, 'YYYY-MON') ORDER BY COUNT(*) DESC) as seqnum,
MIN(d.receiveddate) as min_receiveddate
FROM deliverystaff s JOIN
delivery d
ON d.staffid = s.staffid
WHERE d.receivedDate >= DATE '2020-01-01' AND
d.receivedDate < DATE '2021-01-01'
GROUP BY TO_CHAR(d.receivedDate, 'YYYY-MON') AS Month,
s.staffID, s.staffname
) sd
WHERE seqnum = 1
ORDER BY min_receiveddate;
In addition to the above, this allows you to order the results chronologically and works if you extend the time frame to more than one year.

Reshaping a table SQL

I am using SQL and currently have a large table that contains data for 1000's of accounts sorted by date:
ID July 2018 August 2018 September 2018 …
1 10 20 30
2 50 40 10
3 20 10 80
I need to reshape the table so the table is displayed like this:
ID Month Value
1 July 2018 10
1 August 2018 20
1 September 2018 30
: : :
I don't know how to do this or if this is even possible. I have tried to use the pivot function in SQL but I have not been successful. Is there a way to do this?
You can use APPLY :
SELECT t.id, tt.*
FROM table t CROSS APPLY
( VALUES ('July 2018', [July 2018]),
('August 2018', [August 2018]),
('September 2018', [September 2018])
) tt (Month, Val);
You can use unpivot - it will work in MSSQL
select t.id, up.months, up.value
from tablename t
unpivot
(
value
for months in ([July 2018], [August 2018], [September 2018])
) up;
You can try with the help of UNION operator:
select id, 'July 2018' as Month, July2018 as value from <table>
UNION
select id, 'August 2018' as Month, August2018 as value from <table>
UNION
select id, 'September 2018' as Month, September2018 as value from <table>

SQL transpose full table

I need to do the following transpose in MS SQL
from:
Day A B
---------
Mon 1 2
Tue 3 4
Wed 5 6
Thu 7 8
Fri 9 0
To the following:
Value Mon Tue Wed Thu Fri
--------------------------
A 1 3 5 7 9
B 2 4 6 8 0
I understand how to do it with PIVOT when there is only one column (A) but I can not figure out how to do it when there are multiple columns to transpose (A,B,...)
Example code to be transposed:
select LEFT(datename(dw,datetime),3) as DateWeek,
sum(ACalls) as A,
Sum(BCalls) as B
from DataTable
group by LEFT(datename(dw,datetime),3)
Table Structure:
Column DataType
DateTime Datetime
ACalls int
BCalls int
Any help will be much appreciated.
In order to transpose the data into the result that you want, you will need to use both the UNPIVOT and the PIVOT functions.
The UNPIVOT function takes the A and B columns and converts the results into rows. Then you will use the PIVOT function to transform the day values into columns:
select *
from
(
select day, col, value
from yourtable
unpivot
(
value
for col in (A, B)
) unpiv
) src
pivot
(
max(value)
for day in (Mon, Tue, Wed, Thu, Fri)
) piv
See SQL Fiddle with Demo.
If you are using SQL Server 2008+, then you can use CROSS APPLY with VALUES to unpivot the data. You code would be changed to the following:
select *
from
(
select day, col, value
from yourtable
cross apply
(
values ('A', A),('B', B)
) c (col, value)
) src
pivot
(
max(value)
for day in (Mon, Tue, Wed, Thu, Fri)
) piv
See SQL Fiddle with Demo.
Edit #1, applying your current query into the above solution you will use something similar to this:
select *
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
col,
value
from DataTable
cross apply
(
values ('A', ACalls), ('B', BCalls)
) c (col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv

SQL order pivot result by month

I need to order the rows based on a month or order rather than alphabecially.
When I run the query below I get the following result
Col Mon Tue Wed Thu Fri
--- --- --- --- --- ---
Feb 1 2 3 4 5
Jan 2 3 4 5 6
Mar 3 4 5 6 7
How can I order it as follows:
Col Mon Tue Wed Thu Fri
--- --- --- --- --- ---
Jan 2 3 4 5 6
Feb 1 2 3 4 5
Mar 3 4 5 6 7
My query:
select *
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
col,
value
from DataTable
cross apply
(
values ('Jan', JData), ('Feb', FData), ('Mar', MData)
) c (col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv
At the moment I'm replacing the value's line as:
values ('1.Jan', JData), ('2.Feb', FData), ('3.Mar', MData)
I guess there is a better solution
Try this:
select * from DataTable
order by convert(datetime, '2012/'+ Col +'/01')
SQLFiddle
Thanks Wawrzyniec Sz., I figure it out the solution. Adding an additional column does the trick.
For months the code will be as follows:
select col, Mon, Tue, Wed, Thu, Fri
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
ord,
col,
value
from DataTable
cross apply
(
values ('1','Jan', AVol_Offered), ('2','Feb', OVol_Offered), ('3','Mar', AAHT)
) c (ord, col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv
order by ord
This would apply to other values, for example, if I want to maintain the order listed as C,A,B (which you can not order it asc or des) you will can also use the same code as follows:
select col, Mon, Tue, Wed, Thu, Fri
from
(
select LEFT(datename(dw,datetime),3) as DateWeek,
ord,
col,
value
from DataTable
cross apply
(
values ('1','C', AVol_Offered), ('2','A', OVol_Offered), ('3','B', AAHT)
) c (ord, col, value)
) src
pivot
(
sum(value)
for dateweek in (Mon, Tue, Wed, Thu, Fri)
) piv
order by ord
Thanks for the help/hints