need to sort sql data - sql

iam using sql to build report in report builder, SQL query i am using is as below
select count(*) [Total Clients], li.title,
SUBSTRING(li.title,CHARINDEX('_',li.title,CHARINDEX('_',li.title)+1)+1,2) as month1,
CASE SUBSTRING(li.title,CHARINDEX('_',li.title,CHARINDEX('_',li.title)+1)+1,5)
WHEN '01' THEN 'Jan'
WHEN '02' THEN 'Feb'
WHEN '03' THEN 'Mar'
WHEN '04' THEN 'Apr'
WHEN '05' THEN 'May'
WHEN '06' THEN 'June'
WHEN '07' THEN 'Jul'
WHEN '08' THEN 'Aug'
WHEN '09' THEN 'Sep'
WHEN '10' THEN 'Oct'
WHEN '11' THEN 'Nov'
WHEN '12' THEN 'Dec'
END As [Month],
SUBSTRING(li.title,CHARINDEX('_',li.title)+1,4),
li.CI_UniqueID,coll.name,coll.CollectionID,
SUM (CASE WHEN ucs.status=3 or ucs.status=1 then 1 ELSE 0 END ) as 'Installed / Not Applicable',
sum( case When ucs.status=2 Then 1 ELSE 0 END ) as 'Required',
sum( case When ucs.status=0 Then 1 ELSE 0 END ) as 'Unknown',
round((CAST(SUM (CASE WHEN ucs.status=3 or ucs.status=1 THEN 1 ELSE 0 END) as float)/count(*) )*100,2) as 'Compliant%',
round((CAST(count(case when ucs.status not in('3','1') THEN '*' end) as float)/count(*))*100,2) as 'NotCompliant%'
From v_Update_ComplianceStatusAll UCS
inner join v_r_system sys on ucs.resourceid=sys.resourceid
inner join v_FullCollectionMembership fcm on ucs.resourceid=fcm.resourceid
inner join v_collection coll on coll.collectionid=fcm.collectionid
inner join v_AuthListInfo LI on ucs.ci_id=li.ci_id
where coll.CollectionID like '%SMS00001%' and
--title like '%SUG%'
Title like '%P1%' and
Title like '%SUG_' + '' + CAST(year(getdate())-1 as varchar) + '' + '%'
--or Title like '%SUG_' + '' + CAST(year(getdate())-1 as varchar) + '' + '%'
group by li.title,li.CI_UniqueID,coll.name,coll.CollectionID
data displayed from query
+---------------+---------------------------+--------+-------+------------------+---------------------------------------------------------------------------------------------+----------------------+---------------+-----------------------------+----------+---------------------+---------------+
| Total Clients | title | month1 | Month | (No column name) | CI_UniqueID | name | CollectionID | Installed / Not Applicable | Required | Unknown Compliant% | NotCompliant% |
+---------------+---------------------------+--------+-------+------------------+---------------------------------------------------------------------------------------------+----------------------+---------------+-----------------------------+----------+---------------------+---------------+
| 30 | SUG_2017_01_P1_RFC3456 | 01 | NULL | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_E586ED3A-EDD5-4145-98FB-C0B373F7E4CA | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_01-03_P1_RFC2781 | 01 | NULL | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_5AEB5495-8913-4541-B29E-7D55C16E6B68 | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_03_P1_RFC2781 | 03 | NULL | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_ED07143C-B357-454E-B02E-7D81AEE40869 | All Systems SMS00001 | 0 | 0 | 30 | 0 | 100 |
| 30 | SUG_2017_04_P1_RFC3103 | 04 | NULL | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_2722FDDB-6D6B-407F-A0CE-063372571E82 | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_04-05_P1_RFC2781 | 04 | NULL | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_10D742F8-FB4B-4E19-BF05-5210C790F440 | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_06_P1_RFC3123 | 06 | NULL | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_3063A272-0DF9-4033-94E2-C52AF1CFD4BC | All Systems SMS00001 | 25 | 1 | 4 | 83.33 | 16.67 |
| 30 | SUG_2017_10_P1_RFC3103 | 10 | NULL | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_9C3F338B-E8BA-4AB5-8ECF-1EA8729825DA | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_11_P1_RFC3103 | 11 | NULL | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_B42D69FC-2564-4542-8D5B-F5348A4080FF | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_12_P1_RFC3103 | 12 | NULL | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_5C3AD217-7747-4BAC-AD06-3851014BCB94 | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
+---------------+---------------------------+--------+-------+------------------+---------------------------------------------------------------------------------------------+----------------------+---------------+-----------------------------+----------+---------------------+---------------+
titles shown as
SUG_2017_01_P1_RFC3456
SUG_2017_01-03_P1_RFC2781
SUG_2017_03_P1_RFC2781
i need to show only 01-03 months from these 3 rows i need on 01-03 SUG_2017_01-03_P1_RFC2781 and i need to discard jan row SUG_2017_01_P1_RFC3456 and march row means SUG_2017_03_P1_RFC2781
that applies to all rows i need
i need combine rows and not individual rows.
new data
+---------------+---------------------------+--------+-------+------+---------------------------------------------------------------------------------------------+----------------------+---------------+-----------------------------+----------+---------------------+---------------+
| Total Clients | title | month1 | Month | Year | CI_UniqueID | name | CollectionID | Installed / Not Applicable | Required | Unknown Compliant% | NotCompliant% |
+---------------+---------------------------+--------+-------+------+---------------------------------------------------------------------------------------------+----------------------+---------------+-----------------------------+----------+---------------------+---------------+
| 30 | SUG_2017_01_P1_RFC3456 | 01 | Jan | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_E586ED3A-EDD5-4145-98FB-C0B373F7E4CA | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_01-03_P1_RFC2781 | 01 | Jan | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_5AEB5495-8913-4541-B29E-7D55C16E6B68 | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_03_P1_RFC2781 | 03 | Mar | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_ED07143C-B357-454E-B02E-7D81AEE40869 | All Systems SMS00001 | 11 | 15 | 4 | 36.67 | 63.33 |
| 30 | SUG_2017_04_P1_RFC3103 | 04 | Apr | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_2722FDDB-6D6B-407F-A0CE-063372571E82 | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_04-05_P1_RFC2781 | 04 | Apr | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_10D742F8-FB4B-4E19-BF05-5210C790F440 | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_06_P1_RFC3123 | 06 | June | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_3063A272-0DF9-4033-94E2-C52AF1CFD4BC | All Systems SMS00001 | 25 | 1 | 4 | 83.33 | 16.67 |
| 30 | SUG_2017_10_P1_RFC3103 | 10 | Oct | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_9C3F338B-E8BA-4AB5-8ECF-1EA8729825DA | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_11_P1_RFC3103 | 11 | Nov | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_B42D69FC-2564-4542-8D5B-F5348A4080FF | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2017_12_P1_RFC3103 | 12 | Dec | 2017 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_5C3AD217-7747-4BAC-AD06-3851014BCB94 | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2018_01_P1_RFC3103 | 01 | Jan | 2018 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_A49E2378-BCB6-40BE-BE84-735CCFBEE43F | All Systems SMS00001 | 25 | 1 | 4 | 83.33 | 16.67 |
| 30 | SUG_2018_02_P1_RFC3118 | 02 | Feb | 2018 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_E6CBD108-2B1C-4C94-85F8-57174BEC34C4 | All Systems SMS00001 | 25 | 1 | 4 | 83.33 | 16.67 |
| 30 | SUG_2018_03_P1_RFC3128 | 03 | Mar | 2018 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_6AA69C57-9532-4ED1-BA40-1540C840BD69 | All Systems SMS00001 | 26 | 0 | 4 | 86.67 | 13.33 |
| 30 | SUG_2018_05_P1_RFC3104 | 05 | May | 2018 | ScopeId_A66804AF-F55C-40D6-8AAF-82CF49CC1E5B/AuthList_D3A10469-5DE5-4998-9C59-877D3BC7225F | All Systems SMS00001 | 12 | 14 | 4 | 40 | 60 |
+---------------+---------------------------+--------+-------+------+---------------------------------------------------------------------------------------------+----------------------+---------------+-----------------------------+----------+---------------------+---------------+

Try the following query
WITH commonQueryCTE AS(
-- your query is here
),
paramQueryCTE AS(
SELECT
*,
SUBSTRING(Title,5,4) Y,
SUBSTRING(Title,10,2) M1,
IIF(SUBSTRING(Title,12,1)='-',SUBSTRING(Title,13,2),NULL) M2
FROM commonQueryCTE
)
SELECT *
FROM paramQueryCTE c
WHERE NOT EXISTS(SELECT * FROM paramQueryCTE p WHERE c.Y=p.Y AND c.M1 BETWEEN p.M1 AND p.M2 AND p.M2 IS NOT NULL)
OR c.M2 IS NOT NULL
ORDER BY c.Y,c.M1
SQL Fiddle Demo - http://www.sqlfiddle.com/#!18/bf900/1
As variant you also can use SELECT ... INTO #TempTable FROM ... and after that use #TempTable in that query.

Related

Is there a way to COUNT the amount of non-null between the NULLs in a column?

I have a query that is pulling financial figures and flags whether they have hit a target or not. I have a column that populates a 1 if the target is hit, and it NULLs if the target isn't hit. This is a simple CASE statement.
I need to be able count how many consecutive rows in that column are populated with a 1, and then stop counting when a NULL is hit, and then start counting again from the next non-null.
I have tried every combination of "COUNT(*) OVER" I can possibly think of, all not quite giving me the result I need.
I'll post the entire query as it's not too long -
SELECT
*,
CASE
WHEN zzz.Flag_hit_Target IS NOT NULL THEN COUNT(*) OVER (PARTITION BY zzz.Flag_hit_Target ORDER BY CAST(zzz.Close_month as DATE) DESC)
ELSE NULL
END AS Counter
FROM
(
SELECT
zz.Close_month,
SUM(MRP) as Total_MRP,
zz.Target,
CASE
WHEN SUM(MRP) >= zz.Target THEN 1
ELSE NULL
END AS Flag_hit_target
FROM
(
SELECT
Opp.id,
opp.MRP__c as MRP,
1500 as Target,
CONCAT(DATENAME(month, Closedate), ' ', DATEPART(year, Closedate)) as Close_month
FROM Table1 as Opp WITH (NOLOCK)
WHERE OPP_type__c = 'Opp Type 1'
AND Appointment_setter1__c = 'Person 1'
AND Stagename = 'Closed (Won)'
) as zz
GROUP BY zz.Close_month, zz.Target
) as zzz
ORDER by CAST(zzz.Close_month as DATE) desc
With this I get the following results -
+----------------+-----------------+---------+
| Close_month | Flag_hit_target | Counter |
+----------------+-----------------+---------+
| June 2019 | NULL | NULL |
| April 2019 | NULL | NULL |
| March 2019 | 1 | 1 |
| February 2019 | NULL | NULL |
| January 2019 | 1 | 2 |
| November 2018 | NULL | NULL |
| October 2018 | NULL | NULL |
| September 2018 | NULL | NULL |
| July 2018 | NULL | NULL |
| June 2018 | 1 | 3 |
| May 2018 | NULL | NULL |
| April 2018 | 1 | 4 |
| March 2018 | NULL | NULL |
| February 2018 | 1 | 5 |
| January 2018 | 1 | 6 |
| December 2017 | 1 | 7 |
| October 2017 | NULL | NULL |
| September 2017 | 1 | 8 |
| August 2017 | 1 | 9 |
| July 2017 | 1 | 10 |
| June 2017 | 1 | 11 |
| May 2017 | NULL | NULL |
| April 2017 | 1 | 12 |
| March 2017 | NULL | NULL |
| February 2017 | 1 | 13 |
| January 2017 | 1 | 14 |
+----------------+-----------------+---------+
The results I am after is as following (notice the end column) -
+----------------+-----------------+---------+
| Close_month | Flag_hit_target | Counter |
+----------------+-----------------+---------+
| June 2019 | NULL | NULL |
| April 2019 | NULL | NULL |
| March 2019 | 1 | 1 |
| February 2019 | NULL | NULL |
| January 2019 | 1 | 1 |
| November 2018 | NULL | NULL |
| October 2018 | NULL | NULL |
| September 2018 | NULL | NULL |
| July 2018 | NULL | NULL |
| June 2018 | 1 | 1 |
| May 2018 | NULL | NULL |
| April 2018 | 1 | 1 |
| March 2018 | NULL | NULL |
| February 2018 | 1 | 3 |
| January 2018 | 1 | 2 |
| December 2017 | 1 | 1 |
| October 2017 | NULL | NULL |
| September 2017 | 1 | 4 |
| August 2017 | 1 | 3 |
| July 2017 | 1 | 2 |
| June 2017 | 1 | 1 |
| May 2017 | NULL | NULL |
| April 2017 | 1 | 1 |
| March 2017 | NULL | NULL |
| February 2017 | 1 | 2 |
| January 2017 | 1 | 1 |
+----------------+-----------------+---------+
Thank you!
A solution is to use a ROW_NUMBER for all records, and substract the ROW_NUMBER value of the last NULL record for each date.
Setup:
IF OBJECT_ID('tempdb..#Test') IS NOT NULL
DROP TABLE #Test
CREATE TABLE #Test (
Date DATE,
Flag BIT)
INSERT INTO #Test (
Date,
Flag)
VALUES
('2019-09-01', NULL),
('2019-08-01', NULL),
('2019-07-01', 1),
('2019-06-01', NULL),
('2019-05-01', 1),
('2019-04-01', NULL),
('2019-03-01', NULL),
('2019-02-01', NULL),
('2019-01-01', 1),
('2018-12-01', NULL),
('2018-11-01', 1),
('2018-10-01', NULL),
('2018-09-01', 1),
('2018-08-01', 1),
('2018-07-01', 1),
('2018-06-01', NULL),
('2018-05-01', 1),
('2018-04-01', 1),
('2018-03-01', 1),
('2018-02-01', 1),
('2018-01-01', NULL)
Solution:
;WITH DataWithRowNumber AS
(
SELECT
T.*,
RowNumber = -1 + ROW_NUMBER() OVER (ORDER BY T.Date)
FROM
#Test AS T
)
SELECT
D.Date,
D.Flag,
D.RowNumber,
M.MaxPreviousNullRowNumber,
RowNumberRest = D.RowNumber - M.MaxPreviousNullRowNumber,
Counter = CASE WHEN D.Flag IS NOT NULL THEN D.RowNumber - M.MaxPreviousNullRowNumber END
FROM
DataWithRowNumber AS D
OUTER APPLY (
SELECT
MaxPreviousNullRowNumber = MAX(R.RowNumber)
FROM
DataWithRowNumber AS R
WHERE
R.Date < D.Date AND
R.Flag IS NULL) AS M
ORDER By
D.RowNumber DESC
Result:
+------------+------+-----------+--------------------------+---------------+---------+
| Date | Flag | RowNumber | MaxPreviousNullRowNumber | RowNumberRest | Counter |
+------------+------+-----------+--------------------------+---------------+---------+
| 2019-09-01 | NULL | 20 | 19 | 1 | NULL |
| 2019-08-01 | NULL | 19 | 17 | 2 | NULL |
| 2019-07-01 | 1 | 18 | 17 | 1 | 1 |
| 2019-06-01 | NULL | 17 | 15 | 2 | NULL |
| 2019-05-01 | 1 | 16 | 15 | 1 | 1 |
| 2019-04-01 | NULL | 15 | 14 | 1 | NULL |
| 2019-03-01 | NULL | 14 | 13 | 1 | NULL |
| 2019-02-01 | NULL | 13 | 11 | 2 | NULL |
| 2019-01-01 | 1 | 12 | 11 | 1 | 1 |
| 2018-12-01 | NULL | 11 | 9 | 2 | NULL |
| 2018-11-01 | 1 | 10 | 9 | 1 | 1 |
| 2018-10-01 | NULL | 9 | 5 | 4 | NULL |
| 2018-09-01 | 1 | 8 | 5 | 3 | 3 |
| 2018-08-01 | 1 | 7 | 5 | 2 | 2 |
| 2018-07-01 | 1 | 6 | 5 | 1 | 1 |
| 2018-06-01 | NULL | 5 | 0 | 5 | NULL |
| 2018-05-01 | 1 | 4 | 0 | 4 | 4 |
| 2018-04-01 | 1 | 3 | 0 | 3 | 3 |
| 2018-03-01 | 1 | 2 | 0 | 2 | 2 |
| 2018-02-01 | 1 | 1 | 0 | 1 | 1 |
| 2018-01-01 | NULL | 0 | NULL | NULL | NULL |
+------------+------+-----------+--------------------------+---------------+---------+
Ryan you need to implement the sql running total here , please check the link
https://codingsight.com/calculating-running-total-with-over-clause-and-partition-by-clause-in-sql-server/

Why do you need to include a field in GROUP BY when using OVER (PARTITION BY x)?

I have a table for which I want to do a simple sum of a field, grouped by two columns. I then want the total for all values for each year_num.
See example: http://rextester.com/QSLRS68794
This query is throwing: "42803: column "foo.num_cust" must appear in the GROUP BY clause or be used in an aggregate function", and I cannot figure out why. Why would an aggregate function using the OVER (PARTITION BY x) require the summed field to be in GROUP BY??
select
year_num
,age_bucket
,sum(num_cust)
--,sum(num_cust) over (partition by year_num) --THROWS ERROR!!
from
foo
group by
year_num
,age_bucket
order by 1,2
TABLE:
| loc_id | year_num | gen | cust_category | cust_age | num_cust | age_bucket |
|--------|-----------|------|----------------|-----------|-----------|-------------|
| 1 | 2016 | M | cash | 41 | 2 | 04_<45 |
| 1 | 2016 | F | Prepaid | 41 | 1 | 03_<35 |
| 1 | 2016 | F | cc | 61 | 1 | 05_45+ |
| 1 | 2016 | F | cc | 19 | 2 | 02_<25 |
| 1 | 2016 | M | cc | 64 | 1 | 05_45+ |
| 1 | 2016 | F | cash | 46 | 1 | 05_45+ |
| 1 | 2016 | F | cash | 27 | 3 | 03_<35 |
| 1 | 2016 | M | cash | 42 | 1 | 04_<45 |
| 1 | 2017 | F | cc | 35 | 1 | 04_<45 |
| 1 | 2017 | F | cc | 37 | 1 | 04_<45 |
| 1 | 2017 | F | cash | 46 | 1 | 05_45+ |
| 1 | 2016 | F | cash | 19 | 4 | 02_<25 |
| 1 | 2017 | M | cash | 43 | 1 | 04_<45 |
| 1 | 2017 | M | cash | 29 | 1 | 03_<35 |
| 1 | 2016 | F | cc | 13 | 1 | 01_<18 |
| 1 | 2017 | F | cash | 16 | 2 | 01_<18 |
| 1 | 2016 | F | cc | 17 | 2 | 01_<18 |
| 1 | 2016 | M | cc | 17 | 2 | 01_<18 |
| 1 | 2017 | F | cash | 18 | 9 | 02_<25 |
DESIRED OUTPUT:
| year_num | age_bucket | sum | sum over (year_num) |
|----------|------------|-----|---------------------|
| 2016 | 01_<18 | 5 | 21 |
| 2016 | 02_<25 | 6 | 21 |
| 2016 | 03_<35 | 4 | 21 |
| 2016 | 04_<45 | 3 | 21 |
| 2016 | 05_45+ | 3 | 21 |
| 2017 | 01_<18 | 2 | 16 |
| 2017 | 02_<25 | 9 | 16 |
| 2017 | 03_<35 | 1 | 16 |
| 2017 | 04_<45 | 3 | 16 |
| 2017 | 05_45+ | 1 | 16 |
You need to nest the sum()s:
select year_num, age_bucket, sum(num_cust),
sum(sum(num_cust)) over (partition by year_num) --WORKS!!
from foo
group by year_num, age_bucket
order by 1, 2;
Why? Well, the window function is not doing aggregation. The argument needs to be an expression that can be evaluated after the group by (because this is an aggregation query). Because num_cust is not a group by key, it needs an aggregation function.
Perhaps this is clearer if you used a subquery:
select year_num, age_bucket, sum_num_cust,
sum(sum_num_cust) over (partition by year_num)
from (select year_num, age_bucket, sum(num_cust) as sum_num_cust
from foo
group by year_num, age_bucket
) ya
order by 1, 2;
These two queries do exactly the same thing. But with the subquery it should be more obvious why you need the extra aggregation.

Group By Different Values

I would like to group by the first day and then the rest of the month, I have data that spans years.
I have data like below:
--------------------------------------
DAY MONTH YEAR VISITOR_COUNT
--------------------------------------
1 | 12 | 2014 | 16260
2 | 12 | 2014 | 15119
3 | 12 | 2014 | 14464
4 | 12 | 2014 | 13746
5 | 12 | 2014 | 13286
6 | 12 | 2014 | 14352
7 | 12 | 2014 | 19293
8 | 12 | 2014 | 13338
9 | 12 | 2014 | 13961
10 | 12 | 2014 | 9519
11 | 12 | 2014 | 10204
12 | 12 | 2014 | 9380
13 | 12 | 2014 | 11611
14 | 12 | 2014 | 14839
15 | 12 | 2014 | 10051
16 | 12 | 2014 | 8983
17 | 12 | 2014 | 7348
18 | 12 | 2014 | 7258
19 | 12 | 2014 | 7205
20 | 12 | 2014 | 6113
21 | 12 | 2014 | 5316
22 | 12 | 2014 | 6914
23 | 12 | 2014 | 6880
24 | 12 | 2014 | 6289
25 | 12 | 2014 | 6000
26 | 12 | 2014 | 13328
27 | 12 | 2014 | 10367
28 | 12 | 2014 | 7946
29 | 12 | 2014 | 9042
30 | 12 | 2014 | 9408
31 | 12 | 2014 | 8411
1 | 1 | 2015 | 9965
2 | 1 | 2015 | 10560
3 | 1 | 2015 | 9662
4 | 1 | 2015 | 8735
5 | 1 | 2015 | 12817
6 | 1 | 2015 | 13516
7 | 1 | 2015 | 9800
8 | 1 | 2015 | 10629
9 | 1 | 2015 | 12325
10 | 1 | 2015 | 11899
11 | 1 | 2015 | 11049
12 | 1 | 2015 | 13934
13 | 1 | 2015 | 16833
14 | 1 | 2015 | 13434
15 | 1 | 2015 | 13128
16 | 1 | 2015 | 14660
17 | 1 | 2015 | 11951
18 | 1 | 2015 | 10916
19 | 1 | 2015 | 14126
20 | 1 | 2015 | 16909
21 | 1 | 2015 | 16555
22 | 1 | 2015 | 14726
23 | 1 | 2015 | 14642
24 | 1 | 2015 | 13067
25 | 1 | 2015 | 11738
26 | 1 | 2015 | 15353
27 | 1 | 2015 | 17935
28 | 1 | 2015 | 14448
29 | 1 | 2015 | 15372
30 | 1 | 2015 | 16694
31 | 1 | 2015 | 16763
I would like to be able to group it like below:
--------------------------------------
DAY MONTH YEAR VISITOR_COUNT
--------------------------------------
1 | 12 | 2014 | 16260
2-31| 12 | 2014 | 309971
1 | 1 | 2015 | 9965
2-31| 1 | 2015 | 404176
Microsoft SQL Server 2016. Compatibility level: SQL Server 2005 (90)
Just use case:
select (case when min(day) = 1 then '1'
else concat(min(day), '-', max(day))
end) as day, month, year,
sum(visitor_count)
from t
group by year, month,
(case when day = 1 then 1 else 2 end);
Okay, this is a little tricky. The case in the group by and the case in the select are different. The group by just puts the days into two categories, 1 and others. The select chooses the minimum and maximum days in the month, to construct the range string.
EDIT:
Oy, SQL Server 2005 ???
Of course, you can do the same thing with + and type conversion, or using replace():
select (case when min(day) = 1 then '1'
else replace(replace('#min-#max', '#min', min(day)), '#max', max(day))
end) as day, month, year,
sum(visitor_count)
from t
group by year, month,
(case when day = 1 then 1 else 2 end);

Complex SQL query with pivot

I have the following table.
Data_table
R_id I_id Metric CType Timespan Quantity Date
1 1 S C Week 100 4/5/2015
1 1 Q C Week 200 4/5/2015
1 1 I D Week 80 4/5/2015
1 2 S C Week 150 4/5/2015
1 2 Q C Week 100 4/5/2015
1 2 I D Week 50 4/5/2015
Metric can have a limited set of values (S, Q, I..)
CType will be C, D or nil.
Timespan can be Weekly/Daily.
Date will be a Sunday (start of week) for Weekly and that day's date for Daily.
My goal is to convert this to a daily view which would involve
If Timespan is Daily, copy the Quantity for the above metrics as it is.
Converting a Weekly quantity to 7 Daily quantities.
If the CType is D copy the quantity as it is.
If the CType is C use a constant percentage breakdown logic to distribute the weekly over 7 days.eg [30%, 10%, 10%, 5%, 10%, 15% 20%] = 100%
Creating the following VIEW.
R_id I_id Date S Q I ... (other metrics whose CType is not nil)
1 1 4/5/2015 30 60 80 ... (the quantity of the other metrics)
1 1 4/6/2015 10 20 80
1 1 4/7/2015 10 20 80
1 1 4/8/2015 5 10 80
1 1 4/9/2015 10 20 80
1 1 4/10/2015 15 30 80
1 1 4/11/2015 20 40 80
1 2 4/5/2015 45 30 50
1 2 4/6/2015 15 10 50
1 2 4/7/2015 15 10 50
1 2 4/8/2015 7.5 5 50
1 2 4/9/2015 15 10 50
1 2 4/10/2015 22.5 15 50
1 2 4/11/2015 30 20 50
I can write a bunch of java methods which will pull out the data from the above table and get the values for metrics as needed. But for a large dataset, the performance will not be very good. Databases are meant for this type of data computation. Once this view is created, I can quickly (and simply) query it to get what I want. I can write simple sql queries. But I have no clue how to even begin approaching this problem! I can see a PIVOT here (logically, I don't know how a query would or even can achieve it). But how to compute the 7 daily quantities from a weekly quantity and put it in the VIEW?
Suggestions and guidance will be much appreciated.
You can use hierarchical queries to generate daily data.
SQL Fiddle
Query:
select
r_id,
i_id,
metric,
ctype,
timespan,
quantity,
tdate + level - 1 as m_tdate,
level as m_level,
(case ctype
when 'C' then
(case level
when 1 then 0.3
when 2 then 0.1
when 3 then 0.1
when 4 then 0.05
when 5 then 0.1
when 6 then 0.15
when 7 then 0.2
end)
else 1
end) * quantity as m_quantity
from myt
where timespan = 'Week'
connect by level <= 7
and r_id = prior r_id
and i_id = prior i_id
and metric = prior metric
and ctype = prior ctype
and timespan = prior timespan
and prior sys_guid() is not null
This will generate seven day data for each record
Results:
| R_ID | I_ID | METRIC | CTYPE | TIMESPAN | QUANTITY | M_TDATE | M_LEVEL | M_QUANTITY |
|------|------|--------|-------|----------|----------|-----------------------|---------|------------|
| 1 | 1 | I | D | Week | 80 | May, 04 2015 00:00:00 | 1 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 05 2015 00:00:00 | 2 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 06 2015 00:00:00 | 3 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 07 2015 00:00:00 | 4 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 08 2015 00:00:00 | 5 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 09 2015 00:00:00 | 6 | 80 |
| 1 | 1 | I | D | Week | 80 | May, 10 2015 00:00:00 | 7 | 80 |
| 1 | 1 | Q | C | Week | 200 | May, 04 2015 00:00:00 | 1 | 60 |
| 1 | 1 | Q | C | Week | 200 | May, 05 2015 00:00:00 | 2 | 20 |
| 1 | 1 | Q | C | Week | 200 | May, 06 2015 00:00:00 | 3 | 20 |
| 1 | 1 | Q | C | Week | 200 | May, 07 2015 00:00:00 | 4 | 10 |
| 1 | 1 | Q | C | Week | 200 | May, 08 2015 00:00:00 | 5 | 20 |
| 1 | 1 | Q | C | Week | 200 | May, 09 2015 00:00:00 | 6 | 30 |
| 1 | 1 | Q | C | Week | 200 | May, 10 2015 00:00:00 | 7 | 40 |
| 1 | 1 | S | C | Week | 100 | May, 04 2015 00:00:00 | 1 | 30 |
| 1 | 1 | S | C | Week | 100 | May, 05 2015 00:00:00 | 2 | 10 |
| 1 | 1 | S | C | Week | 100 | May, 06 2015 00:00:00 | 3 | 10 |
| 1 | 1 | S | C | Week | 100 | May, 07 2015 00:00:00 | 4 | 5 |
| 1 | 1 | S | C | Week | 100 | May, 08 2015 00:00:00 | 5 | 10 |
| 1 | 1 | S | C | Week | 100 | May, 09 2015 00:00:00 | 6 | 15 |
| 1 | 1 | S | C | Week | 100 | May, 10 2015 00:00:00 | 7 | 20 |
| 1 | 2 | I | D | Week | 50 | May, 04 2015 00:00:00 | 1 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 05 2015 00:00:00 | 2 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 06 2015 00:00:00 | 3 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 07 2015 00:00:00 | 4 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 08 2015 00:00:00 | 5 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 09 2015 00:00:00 | 6 | 50 |
| 1 | 2 | I | D | Week | 50 | May, 10 2015 00:00:00 | 7 | 50 |
| 1 | 2 | Q | C | Week | 100 | May, 04 2015 00:00:00 | 1 | 30 |
| 1 | 2 | Q | C | Week | 100 | May, 05 2015 00:00:00 | 2 | 10 |
| 1 | 2 | Q | C | Week | 100 | May, 06 2015 00:00:00 | 3 | 10 |
| 1 | 2 | Q | C | Week | 100 | May, 07 2015 00:00:00 | 4 | 5 |
| 1 | 2 | Q | C | Week | 100 | May, 08 2015 00:00:00 | 5 | 10 |
| 1 | 2 | Q | C | Week | 100 | May, 09 2015 00:00:00 | 6 | 15 |
| 1 | 2 | Q | C | Week | 100 | May, 10 2015 00:00:00 | 7 | 20 |
| 1 | 2 | S | C | Week | 150 | May, 04 2015 00:00:00 | 1 | 45 |
| 1 | 2 | S | C | Week | 150 | May, 05 2015 00:00:00 | 2 | 15 |
| 1 | 2 | S | C | Week | 150 | May, 06 2015 00:00:00 | 3 | 15 |
| 1 | 2 | S | C | Week | 150 | May, 07 2015 00:00:00 | 4 | 7.5 |
| 1 | 2 | S | C | Week | 150 | May, 08 2015 00:00:00 | 5 | 15 |
| 1 | 2 | S | C | Week | 150 | May, 09 2015 00:00:00 | 6 | 22.5 |
| 1 | 2 | S | C | Week | 150 | May, 10 2015 00:00:00 | 7 | 30 |
Once you have this, you need to pivot the result, which can be done by simple GROUP BY
Query:
with x as (
select
r_id,
i_id,
metric,
ctype,
timespan,
quantity,
tdate + level - 1 as m_tdate,
level as m_level,
(case ctype
when 'C' then
(case level
when 1 then 0.3
when 2 then 0.1
when 3 then 0.1
when 4 then 0.05
when 5 then 0.1
when 6 then 0.15
when 7 then 0.2
end)
else 1
end) * quantity as m_quantity
from myt
where timespan = 'Week'
connect by level <= 7
and r_id = prior r_id
and i_id = prior i_id
and metric = prior metric
and ctype = prior ctype
and timespan = prior timespan
and prior sys_guid() is not null
UNION ALL
select
r_id,
i_id,
metric,
ctype,
timespan,
quantity,
tdate as m_tdate,
1 as m_level,
quantity as m_quantity
from myt
where timespan = 'Day'
)
select
r_id,
i_id,
m_tdate,
sum(case when metric = 'S' then m_quantity end) S,
sum(case when metric = 'Q' then m_quantity end) Q,
sum(case when metric = 'I' then m_quantity end) I
from x
group by
r_id,
i_id,
m_tdate
order by
r_id,
i_id,
m_tdate
Results:
| R_ID | I_ID | M_TDATE | S | Q | I |
|------|------|-------------------------|--------|--------|-----|
| 1 | 1 | May, 04 2015 00:00:00 | 30 | 60 | 80 |
| 1 | 1 | May, 05 2015 00:00:00 | 10 | 20 | 80 |
| 1 | 1 | May, 06 2015 00:00:00 | 10 | 20 | 80 |
| 1 | 1 | May, 07 2015 00:00:00 | 5 | 10 | 80 |
| 1 | 1 | May, 08 2015 00:00:00 | 10 | 20 | 80 |
| 1 | 1 | May, 09 2015 00:00:00 | 15 | 30 | 80 |
| 1 | 1 | May, 10 2015 00:00:00 | 20 | 40 | 80 |
| 1 | 2 | April, 03 2015 00:00:00 | (null) | (null) | 120 |
| 1 | 2 | May, 04 2015 00:00:00 | 45 | 30 | 50 |
| 1 | 2 | May, 05 2015 00:00:00 | 15 | 10 | 50 |
| 1 | 2 | May, 06 2015 00:00:00 | 15 | 10 | 50 |
| 1 | 2 | May, 07 2015 00:00:00 | 7.5 | 5 | 50 |
| 1 | 2 | May, 08 2015 00:00:00 | 15 | 10 | 50 |
| 1 | 2 | May, 09 2015 00:00:00 | 22.5 | 15 | 50 |
| 1 | 2 | May, 10 2015 00:00:00 | 30 | 20 | 50 |

Grouping / Ordering confusion

Hopefully what I have here is a simple question and explained to you in the correct manner.
I have the following Query:
--DECLARE DATES
DECLARE #Date datetime
DECLARE #DaysInMonth INT
DECLARE #i INT
--GIVE VALUES
SET #Date = Getdate()
SELECT #DaysInMonth = datepart(dd,dateadd(dd,-1,dateadd(mm,1,cast(cast(year(#Date) as varchar)+'-'+cast(month(#Date) as varchar)+'-01' as datetime))))
SET #i = 1
--MAKE TEMP TABLE
CREATE TABLE #TempDays
(
[days] VARCHAR(50)
)
WHILE #i <= #DaysInMonth
BEGIN
INSERT INTO #TempDays
VALUES(#i)
SET #i = #i + 1
END
SELECT #TempDays.days, DATEPART(dd, a.ActualDate) ActualDate, a.ActualAmount, (SELECT SUM(b.ActualAmount)
FROM UnpaidManagement..Actual b
WHERE b.ID <= a.ID) RunningTotal
FROM UnpaidManagement..Actual a
RIGHT JOIN #TempDays on a.ID = #TempDays.days
DROP TABLE #TempDays
Which produces the following output:
+------+------------+--------------+--------------+
| days | ActualDate | ActualAmount | RunningTotal |
+------+------------+--------------+--------------+
| 1 | 1 | 438706 | R 438 706 |
| 2 | 2 | 16239 | R 454 945 |
| 3 | 3 | 1611264 | R 2 066 209 |
| 4 | 4 | 1157777 | R 3 223 986 |
| 5 | 5 | 470662 | R 3 694 648 |
| 6 | 6 | 288628 | 3983276 |
| 7 | 7 | 245897 | 4229173 |
| 8 | 8 | 5235 | 4234408 |
| 9 | 10 | 375630 | 4610038 |
| 10 | 11 | 95610 | 4705648 |
| 11 | 12 | 87285 | 4792933 |
| 12 | 13 | 73399 | 4866332 |
| 13 | 14 | 59516 | 4925848 |
| 14 | 15 | 918915 | 5844763 |
| 15 | 17 | 1957285 | 7802048 |
| 16 | 18 | 489964 | 8292012 |
| 17 | 19 | 272304 | 8564316 |
| 18 | 20 | 378601 | 8942917 |
| 19 | 22 | 92374 | 9035291 |
| 20 | 23 | 198 | 9035489 |
| 21 | 24 | 1500820 | 10536309 |
| 22 | 25 | 2631057 | 13167366 |
| 23 | 26 | 6466505 | 19633871 |
| 24 | 27 | 3757350 | 23391221 |
| 25 | 28 | 3487466 | 26878687 |
| 26 | 29 | 160197 | 27038884 |
| 27 | 30 | 14000 | 27052884 |
| 28 | NULL | NULL | NULL |
| 29 | NULL | NULL | NULL |
| 30 | NULL | NULL | NULL |
| 31 | NULL | NULL | NULL |
+------+------------+--------------+--------------+
If you look closely at the table above, the "ActualDate" column is missing a few values, EG: 9, 16, etc.
And because of this, the rows are being pushed up instead of being grouped with their correct number? How would I accomplish a group by / anything to keep them in their correct row?
DESIRED OUTPUT:
+------+------------+--------------+--------------+
| days | ActualDate | ActualAmount | RunningTotal |
+------+------------+--------------+--------------+
| 1 | 1 | 438706 | R 438 706 |
| 2 | 2 | 16239 | R 454 945 |
| 3 | 3 | 1611264 | R 2 066 209 |
| 4 | 4 | 1157777 | R 3 223 986 |
| 5 | 5 | 470662 | R 3 694 648 |
| 6 | 6 | 288628 | 3983276 |
| 7 | 7 | 245897 | 4229173 |
| 8 | 8 | 5235 | 4234408 |
| 9 | NULL | NULL | NULL |
| 10 | 10 | 375630 | 4610038 |
| 11 | 11 | 95610 | 4705648 |
| 12 | 12 | 87285 | 4792933 |
| 13 | 13 | 73399 | 4866332 |
| 14 | 14 | 59516 | 4925848 |
| 15 | 15 | 918915 | 5844763 |
| 16 | NULL | NULL | NULL |
| 17 | 17 | 1957285 | 7802048 |
| 18 | 18 | 489964 | 8292012 |
| 19 | 19 | 272304 | 8564316 |
| 20 | 20 | 378601 | 8942917 |
| 21 | NULL | NULL | NULL |
| 22 | 22 | 92374 | 9035291 |
| 23 | 23 | 198 | 9035489 |
| 24 | 24 | 1500820 | 10536309 |
| 25 | 25 | 2631057 | 13167366 |
| 26 | 26 | 6466505 | 19633871 |
| 27 | 27 | 3757350 | 23391221 |
| 28 | 28 | 3487466 | 26878687 |
| 29 | 29 | 160197 | 27038884 |
| 30 | 30 | 14000 | 27052884 |
| 31 | NULL | NULL | NULL |
+------+------------+--------------+--------------+
I know this is a long one to read, but please let me know if I have explained this clearly enough. I have been trying to group by this whole morning, but I keep getting errors.
SELECT #TempDays.days, DATEPART(dd, a.ActualDate) ActualDate, a.ActualAmount, (SELECT SUM(b.ActualAmount)
FROM UnpaidManagement..Actual b
WHERE b.ID <= a.ID) RunningTotal
FROM UnpaidManagement..Actual a
RIGHT JOIN #TempDays on DATEPART(dd, a.ActualDate) = #TempDays.days
If you select the temp table as first table in the select and join to UnpaidManagement..Actual you have the days in correct row and order:
SELECT t.days
,DATEPART(dd, a.ActualDate) ActualDate
,a.ActualAmount
,(
SELECT SUM(b.ActualAmount)
FROM UnpaidManagement..Actual b
WHERE b.ID <= a.ID
) RunningTotal
FROM #TempDays AS t
INNER JOIN UnpaidManagement..Actual AS a ON a.IDENTITYCOL = t.days
ORDER BY t.days
After doing that, cou can add CASE WHEN to generate content for the NULL cells.