SQL Query to combine results and show in a PIVOT - sql

Below mentioned are the two of my queries:
SELECT WINDOWS_NT_LOGIN, COUNT(DPS_NUMBER) as TotalDPS
FROM DispatcherProductivity
WHERE DPS_Processed_Time_Stamp>='12/04/2014 10:30 AM'
AND DPS_Processed_Time_Stamp<='12/05/2014 10:30 AM'
GROUP BY WINDOWS_NT_LOGIN
ORDER BY TotalDPS
SELECT STATUS, COUNT(DPS_NUMBER) AS TotalDPS
FROM DispatcherProductivity
WHERE DPS_Processed_Time_Stamp>='12/04/2014 10:30 AM'
AND DPS_Processed_Time_Stamp<='12/05/2014 10:30 AM'
GROUP BY STATUS
ORDER BY TotalDPS
Their respective Results are:
WINDOWS_NT_LOGIN TotalDPS
A_S 72
T_I_S 133
STATUS TotalDPS
ID 1
Can 2
NHD 3
SED 14
Ord 185
I would like to get the results in this format:
WINDOWS_NT_LOGIN ID Can NHD SED Ord
A_S 2 70
T_I_S 1 2 3 12 115
Thanks

You can use the PIVOT function for this:
SELECT pvt.WINDOWS_NT_LOGIN,
pvt.[ID],
pvt.[Can],
pvt.[NHD],
pvt.[SED],
pvt.[Ord]
FROM ( SELECT WINDOWS_NT_LOGIN, STATUS, DPS_NUMBER
FROM DispatcherProductivity
WHERE DPS_Processed_Time_Stamp>='20141204 10:30:00'
AND DPS_Processed_Time_Stamp<='20141205 10:30:00'
) AS t
PIVOT
( COUNT(DPS_NUMBER)
FOR STATUS IN ([ID], [Can], [NHD], [SED], [Ord])
) AS pvt;
N.B. I changed your dates to the culture invariant format yyyyMMdd hh:mm:ss, however, I was not sure if 12/04/2014 was supposed to tbe 12th April, or 4th December (the exact problem with that format), so it is possible I have put the wrong dates in. I assumed you meant 4th December as that is today.
For further reading read Bad habits to kick : mis-handling date / range queries

I tend to use conditional aggregations for pivots. In this case:
SELECT WINDOWS_NT_LOGIN, COUNT(DPS_NUMBER) as TotalDPS,
SUM(CASE WHEN status = 'ID' THEN DPS_Number END) as ID,
SUM(CASE WHEN status = 'Can' THEN DPS_Number END) as Can,
SUM(CASE WHEN status = 'NHD' THEN DPS_Number END) as NHD,
SUM(CASE WHEN status = 'SED' THEN DPS_Number END) as SED,
SUM(CASE WHEN status = 'Ord' THEN DPS_Number END) as Ord
FROM DispatcherProductivity
WHERE DPS_Processed_Time_Stamp >= '12/04/2014 10:30 AM' AND
DPS_Processed_Time_Stamp <= '12/05/2014 10:30 AM'
GROUP BY WINDOWS_NT_LOGIN;
I would also recommend that you use YYYY-MM-DD format for your dates. I, for one, don't know if your dates are for December or April and May.

Related

SQL merging lines into one line

I have a question about an SQL query.
I have a table with these column names:
date time route_id name
(and other columns not included here):
Date
route_id
Time
Name
2022-02-04
320
11:40:00
Taxi
2022-02-04
320
14:35:00
Taxi
I have made the following query:
Select
date,
LEFT(route_id_intern,4) as route_id,
CASE
WHEN time < '12:00:00' THEN 'Morning'
ELSE 'Free' END as 'Morning',
CASE
WHEN time > '12:00:00' THEN 'Afternoon'
ELSE 'Free' END as 'Afternoon',
Name,
FROM [DW2].[dbo].[FCT_RITTEN]
where year(date) = 2022 and month(date)=02 and day(date) = 04
and LEFT(route_id_intern,4) = 3209
My query gives the following result:
Date
route_id
Moring
Afternoon
Name
2022-02-04
320
Morning
Free
Taxi
2022-02-04
320
Free
Afternoon
Taxi
The data stays separated in two lines but I would like to have following result:
Date
route_id
Moring
Afternoon
Name
2022-02-04
320
Morning
Afternoon
Taxi
I have tried several methods but I keep getting these separated lines.
Please note the used data is anonymized for the data in the above samples but the problem stays the same.
Update:
After the reply of #HoneyBadger, I have amended my query:
Select
date,
LEFT(route_id_intern,3) as route_id,
MAX(CASE
WHEN time <= '12:00:00' THEN '1'
ELSE '0' END) as 'Morning',
MAX(CASE
WHEN time > '12:00:00' THEN '1'
ELSE '0' END) as 'Afternoon'
FROM [DW2].[dbo].[FCT_RITTEN]
where date = '2022-02-04'
and LEFT(route_id_intern,4) = 320
group by date, route_id_intern
Unfortunately, the result is still not as needed:
Date
route_id
Morning
Afternoon
2022-02-04
320
1
0
2022-02-04
320
0
1
You are almost there I guess. Removing the group by and putting your query inside a cte and then playing with the time column a bit as below will give you the desired result.
;with cte as(
Select
date,
LEFT(route_id_intern,4) as route_id,
CASE
WHEN max(time)over(partition by date,name order by date) > '12:00:00'
and count(1)over(partition by date,name order by date) > 1
THEN 'Morning-Afternoon'
WHEN max(time)over(partition by date,name order by date) = '12:00:00'
and count(1)over(partition by date,name order by date) > 1
THEN 'Morning-Lunch'
when max(time)over(partition by date,name order by date) > '12:00:00'
and count(1)over(partition by date,name order by date) = 1
then 'Free-Afternoon'
when max(time)over(partition by date,name order by date) < '12:00:00'
and count(1)over(partition by date,name order by date) = 1
then 'Morning-Free'
END as 'day',
Name,
FROM [DW2].[dbo].[FCT_RITTEN]
where year(date) = 2022 and month(date)=02 and day(date) = 04
and LEFT(route_id_intern,4) = 3209
)
select distinct Date,route_id
,SUBSTRING([day],1,charindex('-',[day])-1) as [Morning]
,SUBSTRING([day],charindex('-',[day])+1,len([day])) as [Afternoon]
,name
from cte
One the challenges here is that your sample data and query don't line up. There are columns referenced in your query not in your sample data. I think this is likely because you obfuscated the real information so much that some details got lost. I made some best guesses and I think that what you need is conditional aggregation. At the very least this will return the desired output you wanted based (mostly) on the sample provided.
create table FCT_RITTEN
(
MyDate date
, route_id int
, MyTime time
, Name varchar(10)
)
insert FCT_RITTEN
select '20220204', 320, '11:40:00', 'Taxi' union all
select '20220204', 320, '14:35:00', 'Taxi'
Select
MyDate
, route_id
, Morning = max(CASE WHEN MyTime < '12:00:00' THEN 'Morning' END)
, Afternoon = max(CASE WHEN MyTime > '12:00:00' THEN 'Afternoon' END)
, Name
FROM [dbo].[FCT_RITTEN]
where MyDate = '20220204'
and route_id like '320%' --Why are you doing a string comparison on an integer column?
group by MyDate
, route_id
, Name

Display AM IN, AM OUT, PM IN, PM OUT in a row

I need to return a shift-clock style IN/OUT board from a table of logs.
The expected output is:
ENROLLNO.
DAY
AM_IN
AM_OUT
PM_IN
PM_OUT
1
1
8:36
12:06
1:06
6:36
4
1
7:36
12:06
1:09
7:36
Where:
AM_IN is less than 11:29
AM_OUT is BETWEEN '11:30' AND '12:30'
PM_IN is BETWEEN '12:31' AND '14:00'
PM_OUT is greater than '14:01'
Overall the results should be sorted by ENROLLNO and DAY
So far I have tried this query but it doesn't return the values I need:
ALTER PROCEDURE [dbo].[spViewLOGCSF]
#month nvarchar (max),
#year nvarchar (max)
AS
SELECT
t1.EnrollNo, t1.Day,
MIN(CONVERT(varchar(15), HoursAndMinutes, 100)) AM_IN,
(SELECT MIN(CONVERT(varchar(15), HoursAndMinutes, 100))
FROM tblGLog
WHERE Month = 'April'
AND HoursAndMinutes BETWEEN '11:30' AND '12:30') AM_OUT,
(SELECT MIN(CONVERT(varchar(15), HoursAndMinutes, 100))
FROM tblGLog
WHERE Month = 'April'
AND HoursAndMinutes BETWEEN '12:31' AND '14:00') PM_IN,
MAX(CONVERT(varchar(15), HoursAndMinutes, 100)) PM_OUT
FROM
tblGLog AS t1
LEFT JOIN
tblEnroll AS t2 ON t1.EnrollNo = t2.EnrollNumber
WHERE
t1.Month = #month AND t1.Year = #year
GROUP BY
t1.EnrollNo, t1.Day
The source data table:
Table name: TBLLOGS
Columns are: ENROLLNO, HOURS, DAYS, MONTHS, YEARS , DATETIME
ENROLLNO.
HOURS
DAYS
MONTHS
YEARS
DATETIME
1
8:36
1
APRIL
2021
4/01/2021 8:36:48 AM
1
12:06
1
APRIL
2021
4/01/2021 12:06:48 PM
1
1:06
1
APRIL
2021
4/01/2021 1:06:48 PM
1
5:36
1
APRIL
2021
4/01/2021 5:36:48 PM
1
6:36
1
APRIL
2021
4/01/2021 6:36:48 PM
4
7:36
1
APRIL
2021
4/01/2021 7:36:48 AM
4
12:06
1
APRIL
2021
4/01/2021 12:06:48 PM
4
1:09
1
APRIL
2021
4/01/2021 1:09:48 PM
4
5:32
1
APRIL
2021
4/01/2021 5:36:48 PM
4
7:36
1
APRIL
2021
4/01/2021 7:36:48 PM
Your first issue is that your SP is targeting a different schema to the one you have posted, it is querying tables tblGLog and tblEnroll
For the purposes of this response we will disregard the information provided in the SP script and instead will answer in terms of the posted example data!
You should not use CHAR literals to store your time components, the only columns we need from your example dataset are ENROLLNO and DATETIME, the Day can easily be inferred.
Month and Year can help optimise indexes for some types of execution plans, however your SP is not filtering on Day so you will get more value by storing a DATE typed column for grouping and a TIME typed column for the filtering.
The following solution uses a CTE to transform the dataset, this is not 100% necessary but it illustrates the concrete columns that are required for this type of query. You could optimise your schema by including the additional columns from this CTE into your TBLLOGS schema and setting the values appropriately either as default values, or via triggers or in your INSERT commands.
CROSS APPLY is used to transform the query to apply the specifically requested column expressions, however these columns are not candidates for modifications to the original schema.
NOTE: The time boundary conditions have been closed so that logs at precisely 11:29 and 14:01 are included.
WITH TimePeriodData as
(
SELECT ENROLLNO, [DATETIME], DAYS, HOURS
, CAST([DATETIME] AS Date) AS [Date]
, CAST([DATETIME] AS TIME) AS [TIME]
FROM TBLLOGS
)
SELECT g.ENROLLNO, g.[DATE], g.[DAYS]
, MIN(x.AM_IN) AS AM_IN
, MAX(x.AM_OUT) AS AM_OUT
, MIN(x.PM_IN) AS PM_IN
, MAX(x.PM_OUT) AS PM_OUT
FROM TimePeriodData g
CROSS APPLY (SELECT CASE WHEN [TIME] < '11:30' THEN [HOURS] END AS AM_IN
, CASE WHEN [TIME] BETWEEN '11:30' AND '12:30' THEN [HOURS] END AS AM_OUT
, CASE WHEN [TIME] BETWEEN '12:31' AND '14:00' THEN [HOURS] END AS PM_IN
, CASE WHEN [TIME] > '14:00' THEN [HOURS] END AS PM_OUT
) as x
GROUP BY g.ENROLLNO, g.[Date], g.[DAYS];
See this fiddle: http://sqlfiddle.com/#!18/0073d1/1
Your requirement seems incredibly specific, it would be simpler logic to specify the midday break time as 12:30, then we only have a single conversion per-row. If this value is a constant then it is a candidate for inclusion in the data schema.
This next query deliberately DOES NOT format the output for the DAYS and HOURS columns as requested, instead the SQL query returns the exact data type so that you can easily extend this query to other situations. Formatting the specific output should be handled in the representation layer
WITH TimePeriodData as
(
SELECT ENROLLNO, [DATETIME], DAYS, HOURS
, CAST([DATETIME] AS Date) AS [Date]
, CAST([DATETIME] AS TIME) AS [TIME]
, CASE WHEN DATEPART(hour, [DATETIME]) >=14 THEN 1 ELSE 0 END AS [IS_PM]
FROM TBLLOGS
)
SELECT g.ENROLLNO, g.[DATE]
, MIN(x.AM) AS AM_IN
, MAX(x.AM) AS AM_OUT
, MIN(x.PM) AS PM_IN
, MAX(x.PM) AS PM_OUT
FROM TimePeriodData g
CROSS APPLY (SELECT CASE WHEN [IS_PM] = 0 THEN [TIME] END AS AM
, CASE WHEN [IS_PM] = 1 THEN [TIME] END AS PM
) as x
GROUP BY g.ENROLLNO, g.[Date], g.[DAYS];
This will return: http://sqlfiddle.com/#!18/0073d1/10
ENROLLNO
DATE
AM_IN
AM_OUT
PM_IN
PM_OUT
1
2021-04-01
08:36:48
12:06:48
13:06:48
18:36:48
4
2021-04-01
07:36:48
12:06:48
13:09:48
19:36:48
The following fiddle http://sqlfiddle.com/#!18/b09ee3/2 demonstrates how the query could be simplfied:
SELECT g.ENROLLNO, g.[DATE]
, MIN(x.AM) AS AM_IN
, MAX(x.AM) AS AM_OUT
, MIN(x.PM) AS PM_IN
, MAX(x.PM) AS PM_OUT
FROM TBLLOGS g
CROSS APPLY (SELECT CASE WHEN [IS_PM] = 0 THEN [TIME] END AS AM
, CASE WHEN [IS_PM] = 1 THEN [TIME] END AS PM
) as x
GROUP BY g.ENROLLNO, g.[Date];
There is no example here of how to apply this to the original SP because the exact table schema is not provided, no explanation for the un-used LEFT JOIN reference in that SP and the Hardcoded 'April' filtering ignored the input parameters... there is just too much mess there that I choose not to be involved with ;)

Oracle - Convert Hourly Data from Rows to Columns

I have an Oracle table filled with rows of data associated to datetimes.
EX:
Value Datetime
----- ----------------
123 12/08/2018 00:00
456 12/08/2018 01:00
789 12/08/2018 02:00
... ...
312 12/08/2018 23:00
321 12/09/2018 00:00
... ...
I need to take this data and group it by day, but somehow transpose the hourly data to columns with one column for each hour in a 24 hour period.
EX:
Date Value(00:00) Value(01:00) Value(02:00) ... Value(23:00)
---- ------------ ------------ ------------ --- ------------
12/08/2018 123 456 789 ... 312
12/09/2018 321 654 987 ... 423
... ... ... ... ... ...
What is the most effective way of querying to obtain the data from the table in this format? I apologize if this question has already been answered. I've searched a good bit online, but I'm honestly somewhat at odds of even knowing the right keywords to search for this answer. Thank you in advance.
I've tried to simplify the answer the best I could for stack overflow. I was able to get the information formatted using a query similar to the one below.
SELECT date,
SUM(CASE WHEN EXTRACT(HOUR FROM datetime) >= 0 AND EXTRACT(HOUR FROM datetime) < 1 THEN value ELSE 0 END) as "value(00:00)",
SUM(CASE WHEN EXTRACT(HOUR FROM datetime) >= 1 AND EXTRACT(HOUR FROM datetime) < 2 THEN value ELSE 0 END) as "value(01:00)",
SUM(CASE WHEN EXTRACT(HOUR FROM datetime) >= 2 AND EXTRACT(HOUR FROM datetime) < 3 THEN value ELSE 0 END) as "value(02:00)",
...
FROM (
SELECT TO_CHAR(datetime,'YYYY-MM-DD') AS date,
value,
datetime
FROM table
)
GROUP BY date
The process of taking rows and turning them into columns is called pivoting. As of 11g (?), Oracle has the PIVOT keyword to do this natively. Note there is no "time" datatype in Oracle, so I converted them to two-digit characters before pivoting. There must be an aggregate function in the pivot, so make sure you do not have duplicate rows with the same date.
select * from (
select "Value", to_char( "Datetime", 'HH24' ) as dt from table1
)
pivot ( max( "Value" ) for ( dt ) in ( '00' AS "hour0",
'01' as "hour1", '02' as "hour2" ));
Edit:
To aggregate by the day, ignoring the time component, add a TRUNCed column to the inline view.
select * from (
select "Value", trunc("Datetime") as the_day,
to_char( "Datetime", 'HH24' ) as dt from table1
)
pivot ( max( "Value" ) for ( dt ) in ( '00', '01', '02' ));
SQL Fiddle 2

SQL date (specific date)

I want to get the list of offices name that was working for us before 2015-01-01 and after 2016-01-01 but not between 2015-01-01 and 2016-01-01.
If try to put NOT BETWEEN then it will basically give me the result excluding that two dates.can somebody solve this ?
select distinct office_name
from history
where date not between '01-jan-2015' and '01-jan-2016'
I think you want aggregation:
select office_name
from history
group by office_name
having sum(case when date between date '2015-01-01' and '2016-01-01' then 1 else 0 end) = 0;;

BigQuery: aggregate metrics by running 7d/14d/30d etc. buckets

Anyone aware of a short, neat BQ query (#standardsql) to aggregate metrics (sessions / PVs / users etc.) by running 7d/14d/30d etc. buckets For ex.
16th-22nd April: 300K sessions
9th-15th April: 330K sessions
2nd-8th April: 270K sessions
OR, out-of-the box function that converts GA's date field (STRING) to days_since_epoch
I wrote a query but it's very complicated
- manually extract as YYY, MM, DD components with REGEXP_EXTRACT()
- convert to days_since_epoch using UNIX_DATE
- divide by '7' to group each row into weekly observations
- use GROUP BY to aggregate & report
any pointers to simplify this use case will be highly appreciable !
Cheers!
Anyone aware of a short, neat BQ query (#standardsql) to aggregate metrics (sessions / PVs / users etc.) by running 7d/14d/30d etc. buckets
See below 7d example for BigQuery Standard SQL - you can apply this logic to whatever data you have with hopefully light adjustments
#standardSQL
WITH data AS (
SELECT
day, CAST(1000 * RAND() AS INT64) AS events
FROM UNNEST(GENERATE_DATE_ARRAY('2017-01-01', '2017-04-25')) AS day
)
SELECT
FORMAT_DATE('%U', day) as week,
FORMAT_DATE('%Y, %B %d', MIN(day)) AS start,
FORMAT_DATE('%Y, %B %d', MAX(day)) AS finish,
SUM(events) AS events
FROM data
GROUP BY week
ORDER BY week
It produces below output that can be used as a starting point for further tailoring to your desired layout
week start finish events
01 2017, January 01 2017, January 07 3699
02 2017, January 08 2017, January 14 4008
03 2017, January 15 2017, January 21 3726
... ... ... ...
OR, out-of-the box function that converts GA's date field (STRING) to days_since_epoch
To convert STRING expressed date into date of DATE type - use PARSE_DATE as in below example
#standardSQL
SELECT PARSE_DATE('%Y%m%d', '20170425') AS date
result is
date
2017-04-25
Finally, below is example/template for running 7d/14d/30d etc. buckets
#standardSQL
WITH data AS (
SELECT
DAY, CAST(1000 * RAND() AS INT64) AS events
FROM UNNEST(GENERATE_DATE_ARRAY('2017-01-01', '2017-04-25')) AS DAY
)
SELECT
DAY,
SUM(CASE WHEN period = 7 THEN events END) AS days_07,
SUM(CASE WHEN period = 14 THEN events END) AS days_14,
SUM(CASE WHEN period = 30 THEN events END) AS days_30
FROM (
SELECT
dates.day AS DAY,
periods.period AS period,
SUM(events) AS events
FROM data AS activity
CROSS JOIN (SELECT DAY FROM data GROUP BY DAY) AS dates
CROSS JOIN (SELECT period FROM (SELECT 7 AS period UNION ALL
SELECT 14 AS period UNION ALL SELECT 30 AS period)) AS periods
WHERE dates.day >= activity.day
AND CAST(DATE_DIFF(dates.day, activity.day, DAY) / periods.period AS INT64) = 0
GROUP BY 1,2
)
GROUP BY DAY
ORDER BY DAY DESC
with output as below
DAY days_07 days_14 days_30
2017-04-25 2087 4004 9700
2017-04-24 1947 4165 9611
2017-04-23 1666 4066 9599
2017-04-22 2121 4820 10014
2017-04-21 2885 5421 10192