TSQL query help structuring results - sql

I have a table with the following columns:
timestamp | value | desc
example of the data:
2014-01-27 10:00:00.000 | 100 | 101
2014-01-27 10:00:00.000 | 105 | 101
2014-01-27 11:00:00.000 | 160 | 101
2014-01-27 12:00:00.000 | 200 | 101
...
...
2014-01-28 10:00:00.000 | 226 | 101
2014-01-28 10:00:00.000 | 325 | 101
2014-01-28 11:00:00.000 | 145 | 101
what I would like to obtain is a grouping by the hour part but without merging the period interval.
So that the result will be like this (in the select I will pass a date interval and a condition on the description like desc = '101':
Structure:
hour | count
Data:
10 | 2 (referring to the 20140127)
11 | 1 (referring to the 20140127)
12 | 1 (referring to the 20140127)
...
...
10 | 2 (referring to the 20140128)
11 | 1 (referring to the 20140128)
I thought about using a cursor but I was wondering if it is possible to achieve this result without it.
I'm using SQL server 2012 SP1.
Thanks for your attention.
Bye,
F.

Try this:-
SELECT Count(*) AS [Count],
Datepart(hour, timestamp) AS [Hour]
FROM yourtable
GROUP BY CONVERT(DATE, timestamp),
Datepart(hour, timestamp)
ORDER BY CONVERT(DATE, timestamp)

You may use this. This should work
SELECT DATEPART(hh,timestamp), COUNT(*)
FROM tablename
GROUP BY
DATEPART(hh,timestamp),
DATETIMEFROMPARTS (YEAR(timestamp),MONTH(timestamp),DAY(timestamp),0,0,0,0,0),
desc HAVING desc ='yourvalue'

Related

SQL create rows with dates at the end of quarter from rows with two dates

I have at table, where i need to create a new table based on two dates in the first table. The first table looks like below with the date format YYYY-MM-DD:
Start date | End date | Data
2015-09-01 | 2016-07-15 | 500
2016-05-01 | 2017-01-01 | 600
What I need the table to do is take all the end of quarter dates there might be in between the two dates in each row, and create new rows with the data like this:
Date | Data
2015-09-30 | 500
2015-12-31 | 500
2016-03-31 | 500
2016-06-30 | 500
2016-06-30 | 600
2016-09-30 | 600
2016-12-31 | 600
There are more rows than the two above. Does anyone know how I can do this? Thanks :-)
Here is an approach using a recursive query. The logic is to iterate over the end of quarters until the date range is exhausted:
with cte as (
select
id,
dateadd(qq, datediff(qq, 0, start_date) + 1, -1) start_date,
end_date,
data
from mytable
union all
select
id,
dateadd(qq, datediff(qq, 0, dateadd(month, 4, start_date)), -1),
end_date,
data
from cte
where start_date < dateadd(qq, datediff(qq, 0, end_date), -1)
)
select cast(start_date as date) start_date, data from cte order by id, start_date
I added an id column to uniquely identify each record in the starting table. This is not strictly needed for the query, but it allows to properly order the records in the resultset.
Demo on DB Fiddle
Sample data:
id | start_date | end_date | data
-: | :--------- | :--------- | ---:
1 | 2015-09-01 | 2016-07-15 | 500
2 | 2016-05-01 | 2017-01-01 | 600
Query results:
start_date | data
:--------- | ---:
2015-09-30 | 500
2015-12-31 | 500
2016-03-31 | 500
2016-06-30 | 500
2016-06-30 | 600
2016-09-30 | 600
2016-12-31 | 600

Date Difference Changes to 42000 while using LAG function in SQL Server

Question: Find out no follow-up appointments to the call within the following 7 days for a particular Patient
My query:
select *, DATEDIFF(DAY, (APPOINTMENT_DATE - LAG(APPOINTMENT_DATE)
over (ORDER BY PATIENT_ID)), APPOINTMENT_DATE) as DIFFERENCE from [dbo].
[Appointment Data]
Problems:
1.DIFFERENCE CHANGES to some crazy format because of datetime may be.
2.Is my query right? How do I find difference for each customer? I know I have to apply group by but I am little confused.
PLS HELP!
Dataset:
APPOINTMENT_DATE PATIENT_ID DIFFERENCE
2010-05-06 00:00:00.000 00051101 NULL
2010-04-11 00:00:00.000 00101005 40302
2010-05-06 00:00:00.000 00130521 40277
2010-02-07 00:00:00.000 00130521 40302
It seems that you have several mistakes in your query:
1) You should use column PATIENT_ID in partitioning and order by APPOINTMENT_DATE in LAG function
2) You have unnecessary subtraction in DATEDIFF function
So, your query should be something like:
select
*, datediff(dd, lag(APPOINTMENT_DATE) over (partition by PATIENT_ID order by APPOINTMENT_DATE), APPOINTMENT_DATE)
from
[dbo].[Appointment Data]
select *,
DATEDIFF(DAY, LAG(APPOINTMENT_DATE) over (ORDER BY PATIENT_ID), APPOINTMENT_DATE) as DIFFERENCE
from [dbo].[Appointment Data]
Result:
+-----------------------+------------+------------+
| APPOINTMENT_DATE | PATIENT_ID | DIFFERENCE |
+-----------------------+------------+------------+
| 5/6/2010 12:00:00 AM | 00051101 | null |
| 4/11/2010 12:00:00 AM | 00101005 | -25 |
| 5/6/2010 12:00:00 AM | 00130521 | 25 |
| 2/7/2010 12:00:00 AM | 00130521 | -88 |
+-----------------------+------------+------------+
If you switch the dates, the result will be different.
select *,
DATEDIFF(DAY, APPOINTMENT_DATE, LAG(APPOINTMENT_DATE) over (ORDER BY PATIENT_ID)) as DIFFERENCE
from [dbo].[Appointment Data]
Result:
+-----------------------+------------+------------+
| APPOINTMENT_DATE | PATIENT_ID | DIFFERENCE |
+-----------------------+------------+------------+
| 5/6/2010 12:00:00 AM | 00051101 | null |
| 4/11/2010 12:00:00 AM | 00101005 | 25 |
| 5/6/2010 12:00:00 AM | 00130521 | -25 |
| 2/7/2010 12:00:00 AM | 00130521 | 88 |
+-----------------------+------------+------------+

Cumulative open subscriptions with start_date and end_date on Redshift

I am trying to write a query that will allow to me to count the number of active subscriptions by day in Redshift.
I have the following table:
sub_id | start_date | end_date
---------------------------------------
20001 | 2017-09-01 | NULL
20002 | 2017-08-01 | 2017-08-29
20003 | 2016-01-01 | 2017-04-25
20004 | 2016-07-01 | 2017-09-03
I would like to be able to state, for each date between two dates how many subscriptions are active, such that:
date | active_subs
------------------------
2016-06-30 | 1
2016-07-01 | 2
... |
2017-04-24 | 2
2017-04-25 | 1
... |
2017-07-31 | 1
2017-08-01 | 2
... |
2017-08-28 | 2
2017-08-29 | 1
2017-08-30 | 1
2017-08-31 | 1
2017-09-01 | 2
2017-09-02 | 2
2017-09-03 | 1
I have a reference table from which a query can draw 1 row per day with the table name of date and the relevant column being date.ref_date (in the YYYY-MM-DD format)
Do i write this query using window functions or is there a better way?
Thanks
If I understood you correctly, you don't need nor window functions, joins(except to the date table) or cumulative count. You can do this:
SELECT t.date,
COUNT(s.sub_id) as active_subs
FROM dateTable t
LEFT JOIN YourTable s
ON(t.dateCol between s.start_date
AND COALESCE(s.end_date,<Put A late date here>))
GROUP BY t.date
I would do this as:
with cte as (
select start_date as dte, 1 as inc
from t
union all
select coalesce(end_date, current_date), -1 as inc
from t
)
select dte,
sum(sum(inc)) over (order by dte)
from cte
group by dte
order by dte;
There may be off-by-one errors, depending on whether you count stops on the date given or on the next day.

SQL: Select minimum date difference for each group

Any hints to get the minimum difference between start time and end time per guid with the following data in Microsoft SQL 2014:
id| start time | guid | end time
1 | 2015-04-05 12:00 | a | 2015-04-05 12:30
2 | 2015-04-05 12:10 | a | 2015-04-05 12:15
3 | 2015-04-05 12:20 | a | 2015-04-05 12:30
4 | 2015-04-05 12:30 | b | 2015-04-05 12:35
5 | 2015-04-05 12:40 | b | 2015-04-05 12:55
6 | 2015-04-05 12:50 | c | 2015-04-05 12:55
7 | 2015-04-05 13:00 | c | 2015-04-05 13:25
the output I am looking for is:
id | start time | guid | end time
2 | 2015-04-05 12:10 | a | 2015-04-05 12:15
4 | 2015-04-05 12:30 | b | 2015-04-05 12:35
6 | 2015-04-05 12:50 | c | 2015-04-05 12:55
I have tried grouping by guid and using the DateDiff function, but it didn't work.
try with below query
;with CTE as(
select id, sttime,guid,endtime
row_number() over (partition by guid order by datediff(ss,endtime,sttime))
from tablename
) select * from CTE where rowid =1
This answer looks a bit like Indra's answer, however there is a significant difference. Not using datediff, which will fail if any dates are more than approximate 168 years(or 2147483647 seconds) apart. Also fixed some issues.
;WITH CTE as
(
SELECT
id, start_time, guid, end_time,
row_number() over (partition by guid order by end_time - start_time) rn
FROM
table
)
SELECT
id, start_time, guid, end_time
FROM CTE
WHERE rn = 1
WITH CTE
(
SELECT *,ROW_NUMBER() OVER(PARTITION BY GUID ORDER BY DATEDIFF(SS,STARTTIME,ENDTIME) ASC) AS RN
FROM YOURTABLE
)
SELECT * FROM CTE WHERE RN=1
Use NOT EXIST to return a row if no other row with same guid has less datediff:
select id, start_time, guid, end_time
from tablename t1
where not exists
(select 1 from tablename t2
where t2.guid = t1.guid
and datediff(t2.end_time - t2.start_time) < datediff(t1.end_time - t1.start_time))
Note, I don't know SQL Server, so you'll have to adjust the datediff code above.

Group by date and count of entries

I'll make it short, the table looks like this:
| id (int) | registerDate (DATETIME)
|----------|-----------------
| 1 | 2014-07-29 12:00:00
| 2 | 2014-08-01 12:00:00
| 3 | 2014-08-01 12:00:00
| 4 | 2014-08-01 12:00:00
| 5 | 2014-08-02 12:00:00
| 6 | 2014-08-02 12:00:00
| 7 | 2014-08-04 12:00:00
If today is 2014-08-05, I want results like this:
| registerDate (DATETIME) | count (int)
| 2014-08-04 | 1
| 2014-08-03 | 0
| 2014-08-02 | 2
| 2014-08-01 | 1
| 2014-07-31 | 0
| 2014-07-30 | 0
| 2014-07-29 | 1
So I want the count of registered users in the past week (daily).
I tried to find it out on google (unsuccessfully) - however, I hope you can help.
SELECT registerDate, count(registerDate) FROM [TABLE] WHERE
registerDate between (GETDATE()-7) and GETDATE()
group by registerDate
order by registerDate desc
This will take a table like:
2 |1905-06-26 00:00:00.000
4 |2014-08-03 00:00:00.000
5 |2014-08-02 00:00:00.000
1 |2014-08-01 00:00:00.000
3 |2014-07-01 00:00:00.000
6 |2010-07-01 00:00:00.000
7 |2015-07-01 00:00:00.000
8 |2014-08-28 00:00:00.000
9 |2014-08-26 00:00:00.000
10 |2014-08-26 00:00:00.000
And create:
2014-08-28 00:00:00.000 | 1
2014-08-26 00:00:00.000 | 2
The problem with this is it doesn't show the days that are not in the table.
Give me a little more time, I'll have an updated version.
EDIT:
Now the more complex one...
-- Declare how far back you want to go
DECLARE #DAYSBACK int = 6
-- Select into a temptable
select CONVERT(date, registerDate) as RegDate, count(registerDate) as DateCount
INTO #temptable
from Temp where registerDate between (GETDATE()-6) and GETDATE()
group by registerDate order by registerDate desc
-- Check to see if exists if not, insert row
WHILE #DAYSBACK >= 0 BEGIN
IF NOT EXISTS (select top 1 1 from #temptable
where RegDate= CONVERT(date, (GETDATE()-#DAYSBACK))
group by RegDate)
INSERT INTO #temptable values ((GETDATE()-#DAYSBACK), 0)
SET #DAYSBACK = #DAYSBACK -1
END
-- Select what you want
select * from #temptable order by RegDate desc
-- Drop the table you created.
DROP TABLE #temptable
Using the same table as above, it will output:
Register Date | Date Count
--------------------------
2014-08-28 | 1
2014-08-27 | 0
2014-08-26 | 2
2014-08-25 | 0
2014-08-24 | 0
2014-08-23 | 0
2014-08-22 | 0
Try something like this:
select registerDate = convert(date,t.registerDate) ,
registrations = count(*)
from dbo.my_special_registration_table t
where t.registrationDate >= dateadd(day,-7,convert(date,getdate()))
group by convert(date,t.registerDate)
order by 1
If you try to filter out registrations older than 7 days using something like datediff():
where datediff(day,t.registrationDate,getdate()) <= 7
you turned the column registrationDate into an expression. As a result the query optimizer can't make use of any indices that might apply, thus forcing a table scan. If you table is large, performance is likely to be ... suboptimal.