Sql remove Null Values in sub queries - sql

I am trying to do sub select queries but i am having Null values in my group by
SELECT convert(varchar, dbo.ArretProductionJournee.DateArret, 3) ,
(select
sum (datediff(minute, ArretProductionJournee.HeureDebut, ArretProductionJournee.HeureFin) )
where ArretProductionJournee.EnumArret Like 'HH')
as HH,
(select
sum (datediff(minute, ArretProductionJournee.HeureDebut, ArretProductionJournee.HeureFin) )
where ArretProductionJournee.EnumArret Like 'HI')
as HI,
(select
sum (datediff(minute, ArretProductionJournee.HeureDebut, ArretProductionJournee.HeureFin) )
where ArretProductionJournee.EnumArret Like 'PS')
as PS
FROM
dbo.ArretProductionJournee
where dbo.ArretProductionJournee.DateArret BETWEEN '01/04/2021'and '03/04/2021'
group by ArretProductionJournee.EnumArret, convert(varchar, dbo.ArretProductionJournee.DateArret, 3)
This results like below :
I want to remove those Null Values to have a result like so :
---------------------------
Date Arrêt | HH | HI | PS |
---------------------------
03 / 02/ 21| 0 | 29 | 45 |

I guess you do not need those sub-SELECTs. Use conditional aggregation instead. Try this.
SELECT CONVERT(varchar, DateArret, 3),
SUM(IIF(EnumArret Like 'HH', datediff(minute, HeureDebut, HeureFin, 0)) AS HH,
SUM(IIF(EnumArret Like 'HI', datediff(minute, HeureDebut, HeureFin, 0)) AS HI,
SUM(IIF(EnumArret Like 'PS', datediff(minute, HeureDebut, HeureFin, 0)) AS PS,
FROM dbo.ArretProductionJournee
WHERE DateArret BETWEEN '01/04/2021'and '03/04/2021'
GROUP BY CONVERT(varchar, DateArret, 3)
It's called conditional aggregation because each SUM(IIF(condition, val, 0)) item only adds up rows matching the condition.
I removed the table names from your column names (ArretProductionJournee.HeureDebut becomes HeureDebut) because you only use one table, and because the query is easier to read that way.

I'm pretty sure you just want conditional aggregation:
select convert(date, dbo.ArretProductionJournee.DateArret),
sum(case when apj.EnumArret = 'HH'
then datediff(minute, apj.HeureDebut, apj.HeureFin)
end) as HH,
sum(case when apj.EnumArret = 'HI'
then datediff(minute, apj.HeureDebut, apj.HeureFin)
end) as HI,
sum(case when apj.EnumArret = 'PS'
then datediff(minute, apj.HeureDebut, apj.HeureFin)
end) as PS
from dbo.ArretProductionJournee apj
where apj.DateArret between '2021-04-01' and '2021-04-03'
group by convert(date, apj.DateArret);

Related

Assign Label to Dummy Variable in Aggregate Query

I have the following query which works great. I would simply like to label the output (see below) following the dummy variables I created; '7a-7p' '7p-7a'.
Select
count([SHIFT_Type]) as Count
FROM
(
Select
CASE WHEN Checkin_hour >= 7 and Checkin_hour < 19 then '7a-7p' else '7p-7a' END AS [SHIFT_Type]
FROM (
Select *,
CONVERT(VARCHAR(10),CHECKIN_DATE_TIME,111) as Checkin_date, DATEPART(Hour, CHECKIN_DATE_TIME) as Checkin_hour, DATEPART(DW, CHECKIN_DATE_TIME) as Day_of_Week, [Day] = DATENAME(WEEKDAY, CHECKIN_DATE_TIME),
Row_Number () Over (Partition BY Patient_Fin order BY Patient_Fin) as RowNumber
FROM COVID_TAT
WHERE (CHECKIN_DATE_TIME > #StartDate and CHECKIN_DATE_TIME < #EndDate) and PT_DISCH_DISPO not like '%Error%'
and PT_DISCH_DISPO not like '%no show%' and PT_DISCH_DISPO not like'%Left Without Treatment%' and DOCTORSEE_DATE_TIME not like 'null'
and TRACK_GROUP like '%ED Track%'
)sub
)sub
Group By [SHIFT_Type]
CURRENT OUTPUT
Count
1 64
2 39
DESIRED OUTPUT
Count
7a-7p 64
7p-7a 39
The "label" is already available in the intermediate subquery, and you use it as a
GROUP BY column in the outer query. Just add it to the SELECT clause:
SELECT [SHIFT_Type], count([SHIFT_Type]) as Count
FROM ...
GROUP BY [SHIFT_Type]

SQL Row Order based on Columns

I am trying to implement an order column based on THREADPK1 and Date columns in my query.
Example Results (with desired column on end called date_position :
ThreadSourceKey CourseNumber Date ReadCount Date Position
1518055 0701117023LFC 2016-08-24 18 1
1522610 0701117023LFC 2016-08-24 2 1
5443433 0701117023LFC 2016-08-25 1 1
5443433 0701117023LFC 2016-08-27 1 2
5443344 0701117023LFC 2016-08-21 1 1
5443344 0701117023LFC 2016-08-20 1 2
This is my query: Interested to know how to incorporate the ordering of Date position.
SELECT DISTINCT rs.threadsourcekey,
dc.coursenumber,
CONVERT (DATE, rs.modifieddate, 103) AS 'Date',
Sum(rs.recentreadcount) AS ReadCount
FROM customfinal.rsreportingfactforumreadcounts rs
INNER JOIN #threads threads
ON rs.threadsourcekey = threads.threadsourcekey
INNER JOIN final.dimcourse dc
ON rs.coursekey = dc.coursekey
WHERE rs.coursekey = #CourseKey
AND rs.modifieddate >= Dateadd(day, -7, #DefaultDate)
AND rs.usersourcekey >- 1
AND rs.recentreadcount <> 0
GROUP BY rs.threadsourcekey,
dc.coursenumber,
CONVERT (DATE, rs.modifieddate, 103)
select
ThreadSourceKey,
CourseNumber,
[Date],
ReadCount,
Date_Position = row_number() over (partition by ThreadSourceKey order by [Date])
from
(your sql statement) a
Assuming your query works fine, adding ROW_NUMBER() should get you what you want.
Note that since your GROUP BY has CONVERT (DATE, rs.modifieddate, 103) you need to use exactly that to partition by.
SELECT DISTINCT rs.threadsourcekey,
dc.coursenumber,
CONVERT (DATE, rs.modifieddate, 103) AS 'Date',
Sum(rs.recentreadcount) AS ReadCount,
ROW_NUMBER() OVER (partition by rs.threadsourcekey order by CONVERT (DATE, rs.modifieddate, 103)) AS [DATE POSITION]
FROM customfinal.rsreportingfactforumreadcounts rs
INNER JOIN #threads threads
ON rs.threadsourcekey = threads.threadsourcekey
INNER JOIN final.dimcourse dc
ON rs.coursekey = dc.coursekey
WHERE rs.coursekey = #CourseKey
AND rs.modifieddate >= Dateadd(day, -7, #DefaultDate)
AND rs.usersourcekey >- 1
AND rs.recentreadcount <> 0
GROUP BY rs.threadsourcekey,
dc.coursenumber,
CONVERT (DATE, rs.modifieddate, 103)
Here is a snippet if you want to pick at and see how it works.
SELECT threadsourcekey,
ModDate AS 'Date',
Sum(ReadCount) AS ReadCount ,
ROW_NUMBER() OVER (partition by threadsourcekey order by ModDate) AS [DATE POSITION]
FROM
( VALUES
(1518055, '2016-08-24', 1),
(1518055, '2016-08-24', 1),
(1518055, '2016-08-24', 1),
(1522610, '2016-08-24', 1),
(1522610, '2016-08-24', 1),
(5443433, '2016-08-25', 1),
(5443433, '2016-08-27', 1),
(5443344, '2016-08-21', 1),
(5443344, '2016-08-20', 1)
) As Tbl (ThreadSourceKey, ModDate, ReadCount)
GROUP BY threadsourcekey,
ModDate

Split date column into hour segments

Thank you in advance for taking the time to look at this.
I am looking to take a number of records containing a date field and split them into hour columns with a count in each (sql server).
E.g.
SpecialDateColumn
14/1/15 10:23
14/1/15 11:34
14/1/15 12:45
14/1/15 12:55
I'm looking the results in a single row as follows:
Date 10 11 12 13 etc
14/1/15 1 1 2 0
I've tried to do this using a pivot table, but not had much joy.
Thanks again in advance.
You can do this :
SELECT *
FROM (
SELECT SpecialDateColumn AS [Date]
,DATEPART(HOUR, SpecialDateColumn) [Hour]
FROM < TABLE >
) AL1
PIVOT(COUNT([Hour]) FOR [Hour] IN (
[0]
,[1]
,[2]
,[3]
,[4]
,[5]
,[6]
,[7]
,[8]
,[9]
,[10]
,[11]
,[12]
,[13]
,[14]
,[15]
,[16]
,[17]
,[18]
,[19]
,[20]
,[21]
,[22]
,[23]
)) P;
It is simple enough to write this as conditional aggregation:
select cast(SpecialDateColumn as date) as thedate,
sum(case when datepart(hour, SpecialDateColumn) = 10 then 1 else 0 end) as hour_10,
sum(case when datepart(hour, SpecialDateColumn) = 11 then 1 else 0 end) as hour_11,
sum(case when datepart(hour, SpecialDateColumn) = 12 then 1 else 0 end) as hour_12,
sum(case when datepart(hour, SpecialDateColumn) = 13 then 1 else 0 end) as hour_13
from table t
group by cast(SpecialDateColumn as date)
order by thedate;
This way will always get all the hours but is an example with PIVOT. Other than this you can use dynamic SQL to construct the PIVOT either with CASES like Gordon's example or PIVOT
select
*
from (
select
CONVERT(DATE,h) D,
DATEPART(HOUR,h) H
from (
select
'2014-01-01 10:00:01' h
UNION ALL
select
'2014-01-02 11:00:01'
UNION ALL
select
'2014-01-03 10:00:01'
UNION ALL
select
'2014-01-03 14:00:01'
) T
) SRC
PIVOT(
COUNT(H)
FOR H IN ([0],[1],[2],[3],[4],[5],[6],[7],[8],[9],[10],[11],[12],[13],[14],[15],[16],[17],[18],[19],[20],[21],[22],[23])
) PVT
Pivot is the right way imho ... in the snippet below I have an images Table with a field created_date
select
*
from
(
select
1 as dummy ,
datepart(hh, created_date) as h ,
cast(created_date as date) as d
from images
) as t
pivot( count(t.dummy) for t.h in ([9],[10],[11],[12]) ) as pvt
and the result from query looks lihe this:

SQL: grouping by number of entries and entry date

I have the following table log:
event_time | name |
-------------------------
2014-07-16 11:40 Bob
2014-07-16 10:00 John
2014-07-16 09:20 Bob
2014-07-16 08:20 Bob
2014-07-15 11:20 Bob
2014-07-15 10:20 John
2014-07-15 09:00 Bob
I would like to generate a report, where I can group data by number of entries per day and by entry day. So the resulting report for the table above would be something like this:
event_date | 0-2 | 3 | 4-99 |
-------------------------------
2014-07-16 1 1 0
2014-07-15 2 0 0
I use the following approached to solve it:
Select with grouping in range
How to select the count of values grouped by ranges
If I find answer before anybody post it here, I will share it.
Added
I would like to count a number of daily entries for each name. Then I check to which column this value belongs to, and the I add 1 to that column.
I took it in two steps. Inner query gets the base counts. The outer query uses case statements to sum counts.
SQL Fiddle Example
select event_date,
sum(case when cnt between 0 and 2 then 1 else 0 end) as "0-2",
sum(case when cnt = 3 then 1 else 0 end) as "3",
sum(case when cnt between 4 and 99 then 1 else 0 end) as "4-99"
from
(select cast(event_time as date) as event_date,
name,
count(1) as cnt
from log
group by cast(event_time as date), name) baseCnt
group by event_date
order by event_date
try like this
select da,sum(case when c<3 then 1 else 0 end) as "0-2",
sum(case when c=3 then 1 else 0 end) as "3",
sum(case when c>3 then 1 else 0 end) as "4-66" from (
select cast(event_time as date) as da,count(*) as c from
table1 group by cast(event_time as date),name) as aa group by da
First aggregate in two steps:
SELECT day, CASE
WHEN ct < 3 THEN '0-2'
WHEN ct > 3 THEN '4_or_more'
ELSE '3'
END AS cat
,count(*)::int AS val
FROM (
SELECT event_time::date AS day, count(*) AS ct
FROM tbl
GROUP BY 1
) sub
GROUP BY 1,2
ORDER BY 1,2;
Names should be completely irrelevant according to your description.
Then take the query and run it through crosstab():
SELECT *
FROM crosstab(
$$SELECT day, CASE
WHEN ct < 3 THEN '0-2'
WHEN ct > 3 THEN '4_or_more'
ELSE '3'
END AS cat
,count(*)::int AS val
FROM (
SELECT event_time::date AS day, count(*) AS ct
FROM tbl
GROUP BY 1
) sub
GROUP BY 1,2
ORDER BY 1,2$$
,$$VALUES ('0-2'::text), ('3'), ('4_or_more')$$
) AS f (day date, "0-2" int, "3" int, "4_or_more" int);
crosstab() is supplied by the additional module tablefunc. Details and instructions in this related answer:
PostgreSQL Crosstab Query
This is a variation on a PIVOT query (although PostgreSQL supports this via the crosstab(...) table functions). The existing answers cover the basic technique, I just prefer to construct queries without the use of CASE, where possible.
To get started, we need a couple of things. The first is essentially a Calendar Table, or entries from one (if you don't already have one, they're among the most useful dimension tables). If you don't have one, the entries for the specified dates can easily be generated:
WITH Calendar_Range AS (SELECT startOfDay, startOfDay + INTERVAL '1 DAY' AS nextDay
FROM GENERATE_SERIES(CAST('2014-07-01' AS DATE),
CAST('2014-08-01' AS DATE),
INTERVAL '1 DAY') AS dr(startOfDay))
SQL Fiddle Demo
This is primarily used to create the first step in the double aggregate, like so:
SELECT Calendar_Range.startOfDay, COUNT(Log.name)
FROM Calendar_Range
LEFT JOIN Log
ON Log.event_time >= Calendar_Range.startOfDay
AND Log.event_time < Calendar_Range.nextDay
GROUP BY Calendar_Range.startOfDay, Log.name
SQL Fiddle Demo
Remember that most aggregate columns with a nullable expression (here, COUNT(Log.name)) will ignore null values (not count them). This is also one of the few times it's acceptable to not include a grouped-by column in the SELECT list (normally it makes the results ambiguous). For the actual queries I'll put this into a subquery, but it would also work as a CTE.
We also need a way to construct our COUNT ranges. That's pretty easy too:
Count_Range AS (SELECT text, start, LEAD(start) OVER(ORDER BY start) as next
FROM (VALUES('0 - 2', 0),
('3', 3),
('4+', 4)) e(text, start))
SQL Fiddle Demo
We'll be querying these as "exclusive upper-bound" as well.
We now have all the pieces we need to do the query. We can actually use these virtual tables to make queries in both veins of the current answers.
First, the SUM(CASE...) style.
For this query, we'll take advantage of the null-ignoring qualities of aggregate functions again:
WITH Calendar_Range AS (SELECT startOfDay, startOfDay + INTERVAL '1 DAY' AS nextDay
FROM GENERATE_SERIES(CAST('2014-07-14' AS DATE),
CAST('2014-07-17' AS DATE),
INTERVAL '1 DAY') AS dr(startOfDay)),
Count_Range AS (SELECT text, start, LEAD(start) OVER(ORDER BY start) as next
FROM (VALUES('0 - 2', 0),
('3', 3),
('4+', 4)) e(text, start))
SELECT startOfDay,
COUNT(Zero_To_Two.text) AS Zero_To_Two,
COUNT(Three.text) AS Three,
COUNT(Four_And_Up.text) AS Four_And_Up
FROM (SELECT Calendar_Range.startOfDay, COUNT(Log.name) AS count
FROM Calendar_Range
LEFT JOIN Log
ON Log.event_time >= Calendar_Range.startOfDay
AND Log.event_time < Calendar_Range.nextDay
GROUP BY Calendar_Range.startOfDay, Log.name) Entry_Count
LEFT JOIN Count_Range Zero_To_Two
ON Zero_To_Two.text = '0 - 2'
AND Entry_Count.count >= Zero_To_Two.start
AND Entry_Count.count < Zero_To_Two.next
LEFT JOIN Count_Range Three
ON Three.text = '3'
AND Entry_Count.count >= Three.start
AND Entry_Count.count < Three.next
LEFT JOIN Count_Range Four_And_Up
ON Four_And_Up.text = '4+'
AND Entry_Count.count >= Four_And_Up.start
GROUP BY startOfDay
ORDER BY startOfDay
SQL Fiddle Example
The other option is of course the crosstab query, where the CASE was being used to segment the results. We'll use the Count_Range table to decode the values for us:
SELECT startOfDay, "0 -2", "3", "4+"
FROM CROSSTAB($$WITH Calendar_Range AS (SELECT startOfDay, startOfDay + INTERVAL '1 DAY' AS nextDay
FROM GENERATE_SERIES(CAST('2014-07-14' AS DATE),
CAST('2014-07-17' AS DATE),
INTERVAL '1 DAY') AS dr(startOfDay)),
Count_Range AS (SELECT text, start, LEAD(start) OVER(ORDER BY start) as next
FROM (VALUES('0 - 2', 0),
('3', 3),
('4+', 4)) e(text, start))
SELECT Calendar_Range.startOfDay, Count_Range.text, COUNT(*) AS count
FROM (SELECT Calendar_Range.startOfDay, COUNT(Log.name) AS count
FROM Calendar_Range
LEFT JOIN Log
ON Log.event_time >= Calendar_Range.startOfDay
AND Log.event_time < Calendar_Range.nextDay
GROUP BY Calendar_Range.startOfDay, Log.name) Entry_Count
JOIN Count_Range
ON Entry_Count.count >= Count_Range.start
AND (Entry_Count.count < Count_Range.end OR Count_Range.end IS NULL)
GROUP BY Calendar_Range.startOfDay, Count_Range.text
ORDER BY Calendar_Range.startOfDay, Count_Range.text$$,
$$VALUES('0 - 2', '3', '4+')$$) Data(startOfDay DATE, "0 - 2" INT, "3" INT, "4+" INT)
(I believe this is correct, but don't have a way to test it - Fiddle doesn't seem to have the crosstab functionality loaded. In particular, CTEs probably must go inside the function itself, but I'm not sure....)

Multiple Selects into one select

I'm trying to put some data together for a High Charts Bar chart using ASP.NET. Basically, i have three users who i need to track when they have logged into the system. the variants to be used are:
1) Today
2) This Week
3) Last Week
4) Last Month
So, i've created individual tsql scripts for today and and last week, but i'm now a little stuck on how to combine the two statemets, which will eventually be four.
SELECT Count(*) as CountToday from hitsTable WHERE Convert(date,hitDate) =
Convert(date,GETDATE()) Group by UserId
SELECT count(*) as CountLatWeek from hitTable
where hitDate between (DATEADD(week, DATEDIFF (week,0,GETDATE()),-1))
AND getDate() Group by UserId
Searhing on google, leads me to nested select statements, which all seem to form dependacies with the two statements. However, what i need to do is produce a table of results like this:
EDIT
I've set up a SQL Fiddle, so we can test out the examples
http://www.sqlfiddle.com/#!6/a21ec
the fiddle has tsql for today and tsql for last week (which may need some tweaking)
Select Distinct
UserId
, ( Select Count(*) as CountToday from hitsTable h2
Where h2.UserId = h1.UserId
And Convert(date,hitDate) = Convert(date,GETDATE())
) As CountToday
, ( Select count(*) as CountLatWeek from hitsTable h2
Where h2.UserId = h1.UserId
And hitDate Between DATEADD(dd, -(DATEPART(dw, GetDate())-1)-7, GetDate())
And DATEADD(dd, 7-(DATEPART(dw, GetDate()))-7, GetDate())
) As CountLastWeek
FROM hitsTable h1
Here’s another alternative based on #Avinash comment on the question.
Select
UserId
, CountTodayTable.CountToday
, CountLatWeekTable.CountLatWeek
, ...
FROM hitsTable h1
Inner Join
( Select Count(*) as CountToday from hitsTable h2
Where h2.UserId = h1.UserId
And Convert(date,hitDate) = Convert(date,GETDATE())
) CountTodayTable
On CountTodayTable.UserId = h1.UserId
Inner Join
( Select count(*) as CountLatWeek from hitTable h2
Where h2.UserId = h1.UserId
And hitDate between (DATEADD(week, DATEDIFF (week,0,GETDATE()),-1)) And getDate()
) CountLatWeekTable
On CountLatWeekTable.UserId = h1.UserId
...
Try this query
select
id,
sum(case when Convert(date,hitDate) = Convert(date,GETDATE()) then 1 else 0 end) as as CountToday,
sum(hitDate between (DATEADD(week, DATEDIFF (week,0,GETDATE()),-1)) AND getDate() then 1 else 0 end) as CountLatWeek,
...... -- Add more condition
from
hitsTable
group by
UserId
Edit
select
userid,
sum(case when Convert(date,hitDate) =
Convert(date,GETDATE()) then 1 else 0 end) as cnt
from
hitstable
group by userid
FIDDLE
| USERID | CNT |
|--------|-----|
| User1 | 3 |
| User2 | 0 |