Count Query Required in SQL Server 2008 [duplicate] - sql-server

This question already has answers here:
Count Query required [closed]
(3 answers)
Closed 8 years ago.
ID Time Status
----------- ----------------------- --------------------------------------------------
1 2013-12-24 00:00:00 on
2 2013-12-25 00:00:00 on
3 2013-12-26 00:00:00 on
4 2013-12-27 00:00:00 on
5 2013-12-28 00:00:00 on
6 2013-12-29 00:00:00 on
7 2013-12-30 00:00:00 on
8 2013-12-31 00:00:00 on
9 2013-12-24 00:00:00 off
10 2013-12-25 00:00:00 off
11 2013-12-27 00:00:00 off
12 2013-12-27 00:00:00 on
13 2013-12-27 00:00:00 off
14 2013-12-27 00:00:00 on
15 2013-12-27 00:00:00 off
16 2013-12-28 00:00:00 on
17 2013-12-28 00:00:00 off
18 2013-12-28 00:00:00 on
19 2013-12-29 00:00:00 off
20 2013-12-29 00:00:00 on
21 2013-12-30 00:00:00 off
22 2013-12-30 00:00:00 on
23 2013-12-30 00:00:00 off
24 2013-12-30 00:00:00 on
25 2013-12-30 00:00:00 off
26 2013-12-31 00:00:00 on
27 2013-12-31 00:00:00 off
28 2013-12-31 00:00:00 on
29 2013-12-31 00:00:00 off
30 2013-12-31 00:00:00 on
31 2013-12-31 00:00:00 off
My table name is abc and I want to arrange data datewise and status wise with counts of on and counts of off datewise

Select time, SUM(on_cnt),SUM(off_cnt) FROM
(select time,[on] as on_cnt,[off] as off_cnt from table1
pivot
(
count(Status)
for Status in ([on],[off])
) as p) as st
GROUP BY time
Fiddle

How about something like
SELECT
[Time],
SUM(CASE WHEN [Status] = 'On' THEN 1 ELSE 0 END) CntON,
SUM(CASE WHEN [Status] = 'Off' THEN 1 ELSE 0 END) CntOff
FROM Table
GROUP BY [Time]

Related

postgresql frequency adjustment and data filling

I have a table in PostgreSQL
time
goals
assists
2022-03-01 00:00:00
22
1
2022-03-03 00:00:00
24
2
2022-03-07 00:00:00
25
3
I want to add missing timestamps and fill the goals and assists in, as displayed in the table below
time
goals
assists
2022-03-01 00:00:00
22
1
2022-03-02 00:00:00
22
1
2022-03-03 00:00:00
24
2
2022-03-04 00:00:00
24
2
2022-03-05 00:00:00
24
2
2022-03-06 00:00:00
24
2
2022-03-07 00:00:00
25
3

Oracle - Cycle detected while executing recursive 'WITH' query

I'm doing a basic example of recursive query with oracle sql. I'm computing future months of the format MON-YY. I managed to have a seemingly correct query but I don't understand the break condition with a WITH query.
I'm trying to break on the year value (for example stop when you reach 2020), but it detects a cycle while doing that. If I break on the month value (e.g. December), it works.
Here's my query with a month based break:
with
prochains_mois(mois, annee) as (
select 'sep' as mois, 19 as annee
from dual
union all
select
case mois
when 'jan' then 'fev'
when 'fev' then 'mar'
when 'mar' then 'avr'
when 'avr' then 'mai'
when 'mai' then 'jun'
when 'jun' then 'jui'
when 'jui' then 'aou'
when 'aou' then 'sep'
when 'sep' then 'oct'
when 'oct' then 'nov'
when 'nov' then 'dec'
when 'dec' then 'jan'
end,
case mois
when 'dec' then annee + 1
else annee
end
from prochains_mois r
where mois <> 'dec'
)
select * from prochains_mois;
If I do this, it returns a consistent result.
MOI ANNEE
--- ----------
sep 19
oct 19
nov 19
dec 19
Now if I try to break the recursive query on the year, let's say 2020, so I change the where condition in the with clause to :
where annee < 20
Then I get :
ORA-32044: cycle detected while executing recursive WITH query
I tried to break with a later month to see if my year addition works correctly, it seems to be the case. If I break on march, I get the January and February correctly :
where mois <> 'mar'
gives
MOI ANNEE
--- ----------
sep 19
oct 19
nov 19
dec 19
jan 20
fev 20
mar 20
Use DATEs:
with prochains_mois( value ) as (
select DATE '2019-09-01' from dual
union all
select ADD_MONTHS( value, 1 )
FROM prochains_mois
WHERE value < DATE '2020-12-01'
)
select SUBSTR( TO_CHAR( value, 'mon', 'NLS_DATE_LANGUAGE=FRENCH' ), 1, 3 ) AS mois,
TO_CHAR( value, 'RR' ) AS annee
from prochains_mois;
Output:
MOIS | ANNEE
:--- | :----
sep | 19
oct | 19
nov | 19
dec | 19
jan | 20
fev | 20
mar | 20
avr | 20
mai | 20
jui | 20
jui | 20
aou | 20
sep | 20
oct | 20
nov | 20
dec | 20
or use your query and check that the month and year do not match:
with
prochains_mois(mois, annee) as (
select 'sep' as mois, 19 as annee
from dual
union all
select
case mois
when 'jan' then 'fev'
when 'fev' then 'mar'
when 'mar' then 'avr'
when 'avr' then 'mai'
when 'mai' then 'jun'
when 'jun' then 'jui'
when 'jui' then 'aou'
when 'aou' then 'sep'
when 'sep' then 'oct'
when 'oct' then 'nov'
when 'nov' then 'dec'
when 'dec' then 'jan'
end,
case mois
when 'dec' then annee + 1
else annee
end
from prochains_mois r
where ( mois, annee ) NOT IN ( ( 'dec', 20 ) )
)
select * from prochains_mois;
Output:
MOIS | ANNEE
:--- | ----:
sep | 19
oct | 19
nov | 19
dec | 19
jan | 20
fev | 20
mar | 20
avr | 20
mai | 20
jun | 20
jui | 20
aou | 20
sep | 20
oct | 20
nov | 20
dec | 20
db<>fiddle here
Your main issue is that you're trying to manipulate dates using strings/numbers. Don't do that; if you're working with dates, use dates!
E.g. you can do what you're after like so:
WITH prochains_mois (mnth_dt) AS (SELECT TRUNC(sysdate, 'mm') mnth_dt
FROM dual
UNION ALL
SELECT add_months(mnth_dt, 1) mnth_dt
FROM prochains_mois
WHERE add_months(mnth_dt, 1) < add_months(TRUNC(sysdate, 'yyyy'), 12))
SELECT mnth_dt,
to_char(mnth_dt, 'mon') mois,
to_char(mnth_dt, 'yy') annee
FROM prochains_mois;
MNTH_DT MOIS ANNEE
----------- ---- -----
01/09/2019 sep 19
01/10/2019 oct 19
01/11/2019 nov 19
01/12/2019 dec 19
N.B. You could simplify the predicate in the recursive sub-factored query to mnth_dt < add_months(TRUNC(SYSDATE, 'yyyy'), 11).
This works by taking the start date (here, I've used sysdate) and finding the first of the month (by using the optional second parameter of TRUNC to specify the level we're truncating it to).
Then we simply add a month to each date until we hit the last month of the start date's year.
Only after you've got the dates do you then output the data in the format you require using to_char.

SSRS - Using specific Row Number

I have a SQL query where I am getting the row number for a count of employees per division and per month at the beginning of the month and the end of the month. To do that, I use a payroll end date which is a weekly date. So in essence I have 4 dates where employee counts are shown. Some months have 5 dates which makes the row count for that month 5 instead of 4.
I then need to build an SSRS report to show only the first employee count and the last employee count per division, per month. I have the first number since I am using =IIF(Fields!RowNumber.Value = 1, Fields!EMPCOUNT.Value, 0)
The problem I have now is getting the last employee count where I need to conditionally select a count where row number needs to be 5 if exists or 4 if it doesn't exist. I'm not sure how to get the expression to work in SSRS. Sample data is below.
PRCo EMPCOUNT udDivision PREndDate ROWNUM Type
1 89 Civil 2018-01-06 00:00:00 1 1
1 97 Civil 2018-01-13 00:00:00 2 1
1 97 Civil 2018-01-20 00:00:00 3 1
1 97 Civil 2018-01-27 00:00:00 4 1
1 16 Colorado 2018-01-06 00:00:00 1 1
1 18 Colorado 2018-01-13 00:00:00 2 1
1 14 Colorado 2018-01-20 00:00:00 3 1
1 10 Colorado 2018-01-27 00:00:00 4 1
1 94 Civil 2018-02-03 00:00:00 1 2
1 91 Civil 2018-02-10 00:00:00 2 2
1 92 Civil 2018-02-17 00:00:00 3 2
1 91 Civil 2018-02-24 00:00:00 4 2
1 16 Colorado 2018-02-03 00:00:00 1 2
1 16 Colorado 2018-02-10 00:00:00 2 2
1 18 Colorado 2018-02-17 00:00:00 3 2
1 19 Colorado 2018-02-24 00:00:00 4 2
1 92 Civil 2018-03-03 00:00:00 1 3
1 91 Civil 2018-03-10 00:00:00 2 3
1 88 Civil 2018-03-17 00:00:00 3 3
1 92 Civil 2018-03-24 00:00:00 4 3
1 90 Civil 2018-03-31 00:00:00 5 3
1 19 Colorado 2018-03-03 00:00:00 1 3
1 26 Colorado 2018-03-10 00:00:00 2 3
1 25 Colorado 2018-03-17 00:00:00 3 3
1 27 Colorado 2018-03-24 00:00:00 4 3
1 24 Colorado 2018-03-31 00:00:00 5 3
I would do this in your query rather than trying to get it to work directly in SSRS. There might be a simpler way than this but this is just based on your existing query.
Please note this is untested and just off the top of my head so it may need some editing before it will work.
SELECT * INTO #t FROM YOUR_EXISTING_QUERY
SELECT DISTINCT
PRCo
, udDivision
, YEAR(PREndDate) AS Yr
, MONTH(PREndDate) AS Mnth
, FIRST_VALUE(EMPCOUNT) OVER(PARTITION BY PRCo, udDivision, YEAR(PREndDate), MONTH(PREndDate) ORDER BY ROWNUM) AS OpeningEMPCOUNT
, LAST_VALUE(EMPCOUNT) OVER(PARTITION BY PRCo, udDivision, YEAR(PREndDate), MONTH(PREndDate) ORDER BY ROWNUM) AS CLosing_EMPCOUNT
FROM #t
Yo might need to include Type not sure what this does but you get the idea hopefully.
The FIRST_VALUE and LAST_VALUE functions simply get the first/last value within the partition defined, in your case PRCo, udDivision and then just the year and month portion of the payroll end date, the first and last positions are determined by the order clause, in this case row number.

Count Consecutive Days where value greater than 0

Using SQL Server 2012, I am trying create a query that provides me with, say, the top 10 longest wet (or dry) periods from a climate database.
My temp table provides the following data output:
select monthid as [id], date, rain_today
from #raindays
order by monthid asc, date asc
Output:
id date rain_today
-------------------------------
1 24 Dec 2014 2.4
1 25 Dec 2014 0
1 26 Dec 2014 8.7
1 27 Dec 2014 1.8
1 28 Dec 2014 0.3
1 29 Dec 2014 0
1 30 Dec 2014 0
1 31 Dec 2014 0.3
2 01 Jan 2015 0.3
2 02 Jan 2015 0.3
2 03 Jan 2015 18.3
2 04 Jan 2015 0.3
etc. etc.
I would like to return a ranked table that would count the period where rain_today is > 0, (or rain_today = 0) i.e:
Rank Start_Date End_Date Wet Period
----------------------------------------
1 31 Dec 2014 04 Jan 2015 5
2 26 Dec 2014 28 Dec 2014 3
...
The closest I have got from reviewing other similar queries is the following (this is for dry days):
select
#raindays.monthid as id,
min(#raindays.date) as [FirstDryDay],
max(#raindays.date) as [LatestDryDay],
count(*) as countdays
from
(select
monthid,
coalesce(max(case
when rain_today > '0'
then #raindays.date end), '19000101') as latestdry
from
#raindays
group by
monthid) g
join
#raindays on #raindays.monthid = g.monthid
and #raindays.date > g.latestdry
group by
#raindays.monthid
order by
countdays desc
Output:
id FirstDryDay LatestDryDay countdays
-----------------------------------------------
23 21 Oct 2016 31 Oct 2016 11
21 23 Aug 2016 31 Aug 2016 9
**15 23 Feb 2016 29 Feb 2016 7**
10 25 Sep 2015 30 Sep 2015 6
8 28 Jul 2015 31 Jul 2015 4
24 28 Nov 2016 30 Nov 2016 3
29 29 Apr 2017 30 Apr 2017 2
30 30 May 2017 31 May 2017 2
31 29 Jun 2017 30 Jun 2017 2
20 30 Jul 2016 31 Jul 2016 2
7 29 Jun 2015 30 Jun 2015 2
5 30 Apr 2015 30 Apr 2015 1
11 31 Oct 2015 31 Oct 2015 1
17 30 Apr 2016 30 Apr 2016 1
22 30 Sep 2016 30 Sep 2016 1
As you can see, I don't really want to group by id as I want to be able to span over different months and I'm missing other periods that occur earlier in the month. The actual count is working fine correctly it seems, checking above highlighted period:
id date rain_today
15 22 Feb 2016 3.9
15 23 Feb 2016 0
15 24 Feb 2016 0
15 25 Feb 2016 0
15 26 Feb 2016 0
15 27 Feb 2016 0
15 28 Feb 2016 0
15 29 Feb 2016 0
16 01 Mar 2016 3
Thanks in advance for any help!
Is this what you want???
IF OBJECT_ID('tempdb..#TestData', 'U') IS NOT NULL
DROP TABLE #TestData;
CREATE TABLE #TestData (
id INT NOT NULL ,
[Date] DATE NOT NULL,
Rain_Today DECIMAL(9,2) NOT NULL
);
INSERT #TestData (id, Date, Rain_Today) VALUES
(1, '24 Dec 2014', 2.4),
(1, '25 Dec 2014', 0),
(1, '26 Dec 2014', 8.7),
(1, '27 Dec 2014', 1.8),
(1, '28 Dec 2014', 0.3),
(1, '29 Dec 2014', 0),
(1, '30 Dec 2014', 0),
(1, '31 Dec 2014', 0.3),
(2, '01 Jan 2015', 0.3),
(2, '02 Jan 2015', 0.3),
(2, '03 Jan 2015', 18.3),
(2, '04 Jan 2015', 0.3);
--======================================
WITH
cte_AddRankGroup AS (
SELECT
td.id,
td.Date,
td.Rain_Today,
hr.HasRain,
RankGroup = DENSE_RANK() OVER (PARTITION BY td.id ORDER BY td.Date) -
DENSE_RANK() OVER (PARTITION BY td.id, hr.HasRain ORDER BY td.Date)
FROM
#TestData td
CROSS APPLY ( VALUES (IIF(td.Rain_Today = 0, 0, 1)) ) hr (HasRain)
)
SELECT
arg.id,
BegDate = MIN(arg.Date),
EndDate = MAX(arg.Date),
WetPeriod = IIF(arg.HasRain = 1, 'Wet', 'Dry'),
ConsecutiveDays = COUNT(1)
FROM
cte_AddRankGroup arg
GROUP BY
arg.id,
arg.HasRain,
arg.RankGroup
ORDER BY
arg.id,
MIN(arg.Date);
Results...
id BegDate EndDate WetPeriod ConsecutiveDays
----------- ---------- ---------- --------- ---------------
1 2014-12-24 2014-12-24 Wet 1
1 2014-12-25 2014-12-25 Dry 1
1 2014-12-26 2014-12-28 Wet 3
1 2014-12-29 2014-12-30 Dry 2
1 2014-12-31 2014-12-31 Wet 1
2 2015-01-01 2015-01-04 Wet 4
Edit: Code version using CASE expression in place of IIF...
--======================================
WITH
cte_AddRankGroup AS (
SELECT
td.id,
td.Date,
td.Rain_Today,
hr.HasRain,
RankGroup = DENSE_RANK() OVER (PARTITION BY td.id ORDER BY td.Date) -
DENSE_RANK() OVER (PARTITION BY td.id, hr.HasRain ORDER BY td.Date)
FROM
#TestData td
CROSS APPLY ( VALUES (CASE WHEN td.Rain_Today = 0 THEN 0 ELSE 1 END) ) hr (HasRain)
)
SELECT top 10
arg.id,
BegDate = MIN(arg.Date),
EndDate = MAX(arg.Date),
WetPeriod = CASE WHEN arg.HasRain = 1 THEN 'Wet' ELSE 'Dry' END,
ConsecutiveDays = COUNT(1)
FROM
cte_AddRankGroup arg
WHERE
arg.HasRain = '0' -- Top 10 Dry
--arg.HasRain = '1' -- Top 10 Wet
GROUP BY
arg.id,
arg.HasRain,
arg.RankGroup
ORDER BY
ConsecutiveDays desc, MIN(arg.Date);
Modified original script to produce the Top 10 by each period type which was my ultimate aim (output is from the full dataset):
id BegDate EndDate WetPeriod ConsecutiveDays
31 10 Jun 2017 26 Jun 2017 Dry 17
4 02 Mar 2015 14 Mar 2015 Dry 13
5 12 Apr 2015 24 Apr 2015 Dry 13
20 15 Jul 2016 26 Jul 2016 Dry 12
29 01 Apr 2017 11 Apr 2017 Dry 11
26 17 Jan 2017 27 Jan 2017 Dry 11
23 21 Oct 2016 31 Oct 2016 Dry 11
25 01 Dec 2016 09 Dec 2016 Dry 9
21 10 Aug 2016 18 Aug 2016 Dry 9
21 23 Aug 2016 31 Aug 2016 Dry 9
This problem can be resolved by recursion in this way:
-- this variable is needed to stop the recursion
declare #numrows int=(select count(1) from #raindays)
-- add a row number to the table creating a new table as "tabseq"
;WITH tabseq as (select row_number() over(order by date) as rownum, * from #raindays),
-- apply recursion to tabseq keeping a toggle running totals of wet and dry periods
CTE as
(
select *,
(case when rain_today=0 then 1 else 0 end) as dry,
(case when rain_today>0 then 1 else 0 end) as wet
from tabseq where rownum=1
union all
select s.*,
(case when s.rain_today=0 then cte.dry+1 else 0 end) as dry,
(case when s.rain_today>0 then cte.wet+1 else 0 end) as wet
from tabseq s
join cte on s.rownum=cte.rownum+1
where s.rownum<=#numrows
)
select * from cte
Once you have the table (cte) with the dry/wet accumulators you can order and select from it to suit your output requirements.
Please note that this is assuming consecutive days on the table, if there are gaps then instead of adding +1 on the case statement you may need to add a datediff to one side or the other depending how you consider the missing dates (wet or dry).

MS SQL: Group by date [duplicate]

This question already has answers here:
Sql Date Grouping with avaliable dates in database
(3 answers)
Closed 4 years ago.
ID DateTime EmailCount
93 6/1/2014 00:00:00 4
94 6/2/2014 00:00:00 4
95 6/3/2014 00:00:00 2
96 6/4/2014 00:00:00 2
97 6/5/2014 00:00:00 2
98 6/6/2014 00:00:00 2
99 6/7/2014 00:00:00 2
73 6/8/2014 00:00:00 2
74 6/9/2014 00:00:00 2
75 6/10/2014 00:00:00 4
76 6/11/2014 00:00:00 4
77 6/12/2014 00:00:00 2
78 6/13/2014 00:00:00 2
79 6/14/2014 00:00:00 2
80 6/16/2014 00:00:00 2
81 6/17/2014 00:00:00 4
82 6/18/2014 00:00:00 4
83 6/19/2014 00:00:00 4
84 6/20/2014 00:00:00 4
100 6/21/2014 00:00:00 4
101 6/22/2014 00:00:00 4
102 6/23/2014 00:00:00 4
103 6/24/2014 00:00:00 4
89 6/27/2014 00:00:00 4
90 6/28/2014 00:00:00 4
91 6/29/2014 00:00:00 4
92 6/30/2014 00:00:00 4
104 7/1/2014 00:00:00 4
105 7/2/2014 00:00:00 4
106 7/3/2014 00:00:00 4
121 7/6/2014 00:00:00 2
122 7/7/2014 00:00:00 2
123 7/8/2014 00:00:00 2
Generated Output
Startdate EndDate EmailCount
6/3/2014 00:00:00 6/14/2014 00:00:00 2
6/16/2014 00:00:00 6/16/2014 00:00:00 2
7/6/2014 00:00:00 7/8/2014 00:00:00 2
6/1/2014 00:00:00 6/11/2014 00:00:00 4
6/17/2014 00:00:00 6/24/2014 00:00:00 4
6/27/2014 00:00:00 7/3/2014 00:00:00 4
Here, the generated output is not perfect because I want StartDate to EndDate in groups like: (6/3/2014 to 6/9/2014 and EmailCount = 2) and (6/10/2014 to 6/11/2014 and EmailCount =4) and (6/12/2014 to 6/14/2014 and EmailCount =2). Also, date not in database should not be added to group.
A somewhat complex query to explain, but here goes an attempt;
If the time is always midnight, you could use a common table expression to assign a row number to each row, and group by the difference between the date and row number. As long as the sequence is not broken (ie the dates are consecutive and with the same emailid) they will end up in the same group and an outer query can easily extract the start and end date for each group;
WITH cte AS (
SELECT dateandtime, emailid,
ROW_NUMBER() OVER (PARTITION BY emailid ORDER BY dateandtime) rn
FROM mytable
)
SELECT MIN(dateandtime) start_time,
MAX(dateandtime) end_time,
MAX(emailid) emailid
FROM cte GROUP BY DATEADD(d, -rn, dateandtime) ORDER BY start_time
An SQLfiddle to test with.
If the datetimes are not always midnight, the grouping will fail. If that's the case, you could add a common table expression that converts the datetime to a date as a separate step before running this query.
You're looking for runs of consecutive dates in blocks with the same EmailID. This assumes you have no gaps in the dates. I'm not sure it's the most elegant approach but you can find a lot of stuff on this topic.
with BlockStart as (
select t.StartDate, t.EmailID
from T as t left outer join T as t2
on t2.StartDate = t1.StartDate - 1 and t2.EmailID = t1.EmailID
where t2.StartDate is null
union all
select max(StartDate) + 1, null
from T
) as BlockStart
select
StartDate,
(select min(StartDate) - 1 from BlockStart as bs2 where bs2 > bs.StartDate) as EndDate,
EmailID
from BlockStart as bs
where
EmailID is not null
-- /* or */ exists (select 1 from BlockStart as bs3 where bs3.StartDate > bs.StartDate)

Resources