SQL Sum Multiple values for that day - sql

Here is my table
TIME Status
2012-11-15 8:30:00.000 "WAS_FLOW"
2012-11-15 9:00:00.000 "WAS_FLOW"
2012-11-15 10:30:00.000 "H2_FLOW"
2012-11-11 12:14:00.00 "O23HZ_FLOW"
2012-11-11 8:00.00.000 "AZ_FLOW"
2012-11-12 9:00.000 "BZ_FLOW"
I want my results to show:
TIME "WAS FLOW"
2012-11-11 0
2012-11-12 0
2012-11-15 2

Other answers here all had slight glitches. Here is one that I hope will work straight out of the box for you.
SELECT
DATEADD(DAY, DATEDIFF(DAY, 0, [time]), 0) AS [date],
SUM(CASE WHEN status = 'WAS_FLOW' THEN 1 ELSE 0 END) AS [count]
FROM
yourTable
GROUP BY
DATEADD(DAY, DATEDIFF(DAY, 0, [time]), 0)

SELECT CAST(TIME AS date) AS TIME,
COUNT(CASE WHEN Status = 'WAS_FLOW' THEN Status END) AS 'WAS_FLOW'
FROM dbo.your_table
GROUP BY CAST(TIME AS date)
Demo on SQLFiddle

Try below:
SELECT DISTINCT CONVERT(VARCHAR(10), a.TIME, 101),
CASE b.COUNT WHEN NULL THEN 0 ELSE b.COUNT END AS "WAS FLOW"
FROM MyTable AS a
LEFT JOIN
(SELECT Count(STATUS) AS "COUNT"
FROM MyTable
WHERE Status = 'WAS_FLOW'
GROUP BY Time
) AS b
ON a.TIME= b.TIME;

Related

Select data where sum for last 7 from max-date is greater than x

I have a data set as such:
Date Value Type
2020-06-01 103 B
2020-06-01 100 A
2020-06-01 133 A
2020-06-11 150 A
2020-07-01 1000 A
2020-07-21 104 A
2020-07-25 140 A
2020-07-28 1600 A
2020-08-01 100 A
Like this:
Type ISHIGH
A 1
B 0
Here's the query i tried,
select type, case when sum(value) > 10 then 1 else 0 end as total_usage
from table_a
where (select sum(value) as usage from tableA where date = max(date)-7)
group by type, date
This is clearly not right. What is a simple way to do this?
It is a simply group by except that you need to be able to access max date before grouping:
select type
, max(date) as last_usage_date
, sum(value) as total_usage
, case when sum(case when date >= cutoff_date then value end) >= 1000 then 'y' end as [is high!]
from t
cross apply (
select dateadd(day, -6, max(date))
from t as x
where x.type = t.type
) as ca(cutoff_date)
group by type, cutoff_date
If you want just those two columns then a simpler approach is:
select t.type, case when sum(value) >= 1000 then 'y' end as [is high!]
from t
left join (
select type, dateadd(day, -6, max(date)) as cutoff_date
from t
group by type
) as a on t.type = a.type and t.date >= a.cutoff_date
group by t.type
Find the max date by type. Then used it to find last 7 days and sum() the value.
with
cte as
(
select [type], max([Date]) as MaxDate
from tableA
group by [type]
)
select c.[type], sum(a.Value),
case when SUM(a.Value) > 1000 then 1 else 0 end as ISHIGH
from cte c
inner join tableA a on a.[type] = c.[type]
and a.[Date] >= DATEADD(DAY, -7, c.MaxDate)
group by c.[type]
This can be done through a cumulative total as follows:
;With CTE As (
Select [type], [date],
SUM([value]) Over (Partition by [type] Order by [date] Desc) As Total,
Row_Number() Over (Partition by [type] Order by [date] Desc) As Row_Num
From Tbl)
Select Distinct CTE.[type], Case When C.[type] Is Not Null Then 1 Else 0 End As ISHIGH
From CTE Left Join CTE As C On (CTE.[type]=C.[type]
And DateDiff(dd,CTE.[date],C.[date])<=7
And C.Total>1000)
Where CTE.Row_Num=1
I think you are quite close with you initial attempt to solve this. Just a tiny edit:
select type, case when sum(value) > 1000 then 1 else 0 end as total_usage
from tableA
where date > (select max(date)-7 from tableA)
group by type

Get Last and Next Appointments

I have the following table in SQL Server and would like to get the last and next appointments for each customer.
Note: If the first appointment is in the future, last appointment should be N/A. Similarly if the last appointment is in the past, next appointment will be N/A. If the last appointment is older than 30 days it should not be shown (if there is no future appointment - considered an inactive customer).
CustomerId (int) | Date (date) | Time (time)
1 | 20210801 | 11:00
1 | 20210802 | 13:00
1 | 20210805 | 10:00
1 | 20210811 | 16:00
1 | 20210821 | 17:00
2 | 20210801 | 11:00
2 | 20210802 | 11:00
2 | 20210803 | 11:00
2 | 20210804 | 11:00
3 | 20210831 | 11:00
4 | 20210526 | 10:00
In this case the result should be (Assuming the date is today 7 August 2021):
CustomerId (int) | LastAppointment (varchar) | NextAppointment (varchar)
1 | 05 Aug 2021 - 10:00 | 11 Aug 2021 - 16:00
2 | 04 Aug 2021 - 11:00 | N/A
3 | N/A | 31 Aug 2021 - 11:00
Can anyone help me please? An example would be appreciated.
You simply need to work with datetime values and then use conditional aggregation to select the required date for each customer. Using a CTE first to simplify converting the dates as much as possible, this looks like:
with ap as (
select CustomerId, Convert(datetime,Left(Concat([date], ' ', [time]),15)) app
from t
), groups as (
select CustomerId,
Max(case when app <= GetDate() then app end) LastAppointment,
Min(case when app > GetDate() then app end) NextAppointment
from ap
group by customerId
)
select CustomerID,
IsNull(Format(LastAppointment, 'dd MMM yyyy - hh:mm'), 'N/A') LastAppointment,
IsNull(Format(NextAppointment, 'dd MMM yyyy - hh:mm'), 'N/A') NextAppointment
from groups
where DateAdd(day,-30,GetDate()) < isnull(lastappointment,GetDate())
see DB<>Fiddle
Also note this query only touches the table once and performs a single logical read.
You need conditional aggregation:
SELECT CustomerId,
COALESCE(
MAX(CASE
WHEN CAST(Date AS DATETIME) + CAST(Time AS DATETIME) < GETDATE()
THEN FORMAT(CAST(Date AS DATETIME) + CAST(Time AS DATETIME), 'dd MMM yyyy - HH:mm')
END
), 'N/A'
) LastAppointment,
COALESCE(
MIN(CASE
WHEN CAST(Date AS DATETIME) + CAST(Time AS DATETIME) > GETDATE()
THEN FORMAT(CAST(Date AS DATETIME) + CAST(Time AS DATETIME), 'dd MMM yyyy - HH:mm')
END
), 'N/A'
) NextAppointment
FROM tablename
GROUP BY CustomerId
HAVING COALESCE(DATEDIFF(
d,
MAX(CASE
WHEN CAST(Date AS DATETIME) + CAST(Time AS DATETIME) < GETDATE()
THEN CAST(Date AS DATETIME) + CAST(Time AS DATETIME)
END
),
GETDATE()
), 0) < 30
See the demo.
Results:
CustomerId
LastAppointment
NextAppointment
1
05 Aug 2021 - 10:00
11 Aug 2021 - 16:00
2
04 Aug 2021 - 11:00
N/A
3
N/A
31 Aug 2021 - 11:00
NOTE : This solution works but it is very bad in terms of performance, check this answer for a better approach
Something like this
SELECT DISTINCT customerid,
Isnull(CONVERT(VARCHAR,
(SELECT TOP 1 Concat(date, ' ', TIME)
FROM appointments B
WHERE b.customerid = a.customerid
AND ([date] < CONVERT(DATE, Getdate())
OR ([date] = CONVERT(DATE, Getdate())
AND [time] <= CONVERT(TIME, Getdate())))
ORDER BY [date] DESC)), 'N/A') AS lastappointment,
Isnull(CONVERT(VARCHAR,
(SELECT TOP 1 Concat(date, ' ', TIME)
FROM appointments B
WHERE b.customerid = a.customerid
AND ([date] > CONVERT(DATE, Getdate())
OR ([date] = CONVERT(DATE, Getdate())
AND [time] > CONVERT (TIME, Getdate())))
ORDER BY [date])), 'N/A') AS nextappointment
FROM appointments A
WHERE Datediff(DAY,
(SELECT TOP 1 date
FROM appointments B
WHERE b.customerid = a.customerid
AND [date] <= CONVERT(DATE, Getdate())
ORDER BY [date] DESC), CONVERT(DATE, Getdate())) <= 30
OR (((
(SELECT TOP 1 date
FROM appointments B
WHERE b.customerid = a.customerid
AND [date] > CONVERT(DATE, Getdate())
ORDER BY [date]) > CONVERT(DATE, Getdate())))
OR ((
(SELECT TOP 1 date
FROM appointments B
WHERE b.customerid = a.customerid
AND [date] > CONVERT(DATE, Getdate())
ORDER BY [date]) = CONVERT(DATE, Getdate()))
AND (
(SELECT TOP 1 [time]
FROM appointments B
WHERE b.customerid = a.customerid
AND [date] > CONVERT(DATE, Getdate())
ORDER BY [date]) > CONVERT(TIME, Getdate()))))
I called your table appointments and the condition is to select customer with last appointment in the past 30 days OR with a future appointment.
I tested with column types Date for Date and Time(7) for time.
Base table is used only single time because of optimization purpose. Use LAG() function and others necessary condition for picking actual set of data.
-- SQL SERVER
SELECT p.CustomerId
, CASE WHEN p.chk_condition = 1
THEN CONVERT(varchar(13), p.prev_Date, 113) + ' - ' + LEFT(p.prev_time, 5)
WHEN p.chk_condition = 2
THEN CONVERT(varchar(13), p.Date, 113) + ' - ' + LEFT(p.time, 5)
ELSE 'N/A'
END "LastAppointment"
, CASE WHEN p.chk_condition != 2
THEN CONVERT(varchar(13), p.Date, 113) + ' - ' + LEFT(p.time, 5)
ELSE 'N/A'
END "NextAppointment"
FROM ( SELECT t.*
, CASE WHEN t.prev_Date < GETDATE() AND t.Date >= GETDATE()
THEN 1
WHEN t.prev_Date < GETDATE() AND t.Date <= GETDATE()
THEN 2
ELSE 0
END chk_condition
, ROW_NUMBER() OVER (PARTITION BY CustomerId ORDER BY t.Date DESC, t.prev_Date DESC) row_num
FROM (SELECT CustomerId, Date, Time
, LAG(Date) OVER (PARTITION BY CustomerId ORDER BY "Date", "Time") "prev_Date"
, LAG(Time) OVER (PARTITION BY CustomerId ORDER BY "Date", "Time") "prev_Time"
FROM appointment) t
WHERE CASE WHEN t.prev_Date < GETDATE() AND t.Date >= GETDATE()
THEN 1
WHEN t.prev_Date IS NULL
THEN CASE WHEN DATEDIFF(day, t.Date, GETDATE()) >= 30
THEN 0
ELSE 1
END
WHEN t.prev_Date < GETDATE() AND t.Date <= GETDATE()
THEN 1
END = 1 ) p
WHERE p.row_num = 1
ORDER BY p.CustomerId;
Please check this url https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=3813d09cf25ed14d249970654995b085

How to PIVOT with 2 grouping columns in the result set?

I have a query that outputs the following:
ApptDate Truck_ID Item Qty
'8-19-20' TruckA ItemA 100
'8-19-20' TruckB ItemA 200
'8-20-20' TruckC ItemB 300
'8-20-20' TruckD ItemB 400
...
I need to PIVOT so that it returns this:
Item Truck_ID Day1 Day2 ... Day14
ItemA TruckA 100 0 0
ItemA TruckB 200 0 0
ItemB TruckC 0 300 0
ItemB TruckD 0 400 0
I tried this, but it gave an error:
Msg 8114, Level 16, State 1, Line 413
Error converting data type nvarchar to datetime.
Msg 473, Level 16, State 1, Line 413
The incorrect value "Day1" is supplied in the PIVOT operator.
select
item, truck_id, Day1, Day2, Day3, Day4, Day5, Day6, Day7, Day8, Day9, Day10, Day11, Day12, Day13, Day14
from(
select
ds.ApptDate
, c.truck_id
, c.item
, sum(c.qty) qty
from
maintable c with(nolock)
inner join secondtable ds with(nolock) on c.truck_id = ds.truckid and ds.type = 'O'
where
ds.apptdate between cast(getdate() as date) and dateadd(day, 14, cast(getdate() as date))
and coalesce(ds.CancelTruck, 0) <> 1
and ds.Status <> '5'
group by
c.truck_id
, c.item
, ds.ApptDate
) sourcetable
pivot
(
sum(qty)
for apptdate in ([Day1], [Day2], [Day3], [Day4], [Day5], [Day6], [Day7], [Day8], [Day9], [Day10], [Day11], [Day12], [Day13], [Day14])
) as pivottable
Since you expect a fixed number of columns, we don't necessarily need dynamic SQL. One option uses conditional aggregation... and lot of repeated typing:
select
item,
truck_id,
sum(case when appt_date = cast(getdate() as date) then qty else 0 end) day0,
sum(case when appt_date = dateadd(day, -1 , cast(getdate() as date)) then qty else 0 end) day1,
sum(case when appt_date = dateadd(day, -2 , cast(getdate() as date)) then qty else 0 end) day2,
...
sum(case when appt_date = dateadd(day, -14, cast(getdate() as date)) then qty else 0 end) day14
from ( -- your current query here --) t
group by item, truck_id
This approach uses datediff's on the minimum date and the AppDate.
;with
min_dt_cte(min_dt) as (select min(cast(AppDate as date)) from MyTable),
pvt_dt_cte(ApptDate, Truck_ID, Item, Qty, DayNum) as (
select t.*, datediff(d, mdc.min_dt, cast(AppDate as date))
from min_dt_cte mdc
cross join
MyTable t)
select
pdc.Item, pdc.Truck_ID,
iif(pdc.DayNum=1, Qty, 0) Day1,
iif(pdc.DayNum=2, Qty, 0) Day2,
...
iif(pdc.DayNum=14, Qty, 0) Day14
from
pvt_dt_cte pdc;

SQL to calculate time difference between two different rows and column

I have a table in SQL Server 2005 with two fields datetime, one is Start other End.
Example
select Start , End from launchings where id = 210423 order by 1 asc
My result is
2013-11-01 08:30:00.000 2013-11-01 12:00:00.000
2013-11-01 13:00:00.000 2013-11-01 19:00:00.000
2013-11-01 19:00:00.000 2013-11-01 20:00:00.000
2013-11-01 19:00:00.000 2013-11-01 20:00:00.000
2013-11-04 08:30:00.000 2013-11-04 12:00:00.000
2013-11-04 13:00:00.000 2013-11-04 19:30:00.000
I need get the first and the last time a day and the interval between them, which would for example lunchtime
Example
Day 04 - Result that I want
Day Start Start interval End interval End
2013-11-04 - 08:00 12:00 13:00 19:30
2013-11-01 - 08:30 12:00 13:00 20:00
Start and End I did. I need Interval
SELECT
convert(char(10), DATEADD(DAY, DATEDIFF(DAY, 0, Inicio), 0),103) AS Day,
MIN(convert(char(5),Inicio,108)) AS MinDate,
MAX(convert(char(5),Fim,108)) AS MaxDate
from Lancamentos where matricula = 210423
GROUP BY
DATEADD(DAY, DATEDIFF(DAY, 0, Inicio), 0)
Result
Day MinDate MaxDate
01/11/2013 08:30 20:00
04/11/2013 08:30 19:30
The key to solving this is to use ROW_NUMBER() to put your results in order by day:
WITH RankedData AS
( SELECT [Date] = CAST(Start AS DATE),
[Start],
[End],
RowNum = ROW_NUMBER() OVER(PARTITION BY ID, CAST(Start AS DATE) ORDER BY Start)
FROM Launchings
WHERE ID = 210423
)
SELECT Date,
[Start1] = MIN(CASE WHEN RowNum = 1 THEN Start END),
[End1] = MIN(CASE WHEN RowNum = 1 THEN [End] END)
[Start2] = MIN(CASE WHEN RowNum > 1 THEN Start END),
[End2] = MAX(CASE WHEN RowNum > 1 THEN [End] END)
FROM RankedData
GROUP BY Date;
EDIT
Sorry, missed the SQL-Server 2005 part of the question:
WITH RankedData AS
( SELECT [Date] = CAST(Start AS DATE),
[Start],
[End],
RowNum = ROW_NUMBER() OVER(PARTITION BY ID, DATEADD(DAY, DATEDIFF(DAY, 0, Start), 0) ORDER BY Start)
FROM Launchings
WHERE ID = 210423
)
SELECT Date,
[Start1] = MIN(CASE WHEN RowNum = 1 THEN Start END),
[End1] = MIN(CASE WHEN RowNum = 1 THEN [End] END)
[Start2] = MIN(CASE WHEN RowNum > 1 THEN Start END),
[End2] = MAX(CASE WHEN RowNum > 1 THEN [End] END)
FROM RankedData
GROUP BY Date;
--GarethD you still missed the SQL 2005 part of the question on the second line.
--But I think it's easy you should meant something like this:
--Didn't test it.`
WITH RankedData AS
( SELECT [Date] = DATEADD(DAY, DATEDIFF(DAY, 0, Start), 0),
[Start],
[End],
RowNum = ROW_NUMBER() OVER(PARTITION BY ID, DATEADD(DAY, DATEDIFF(DAY, 0, Start), 0) ORDER BY Start)
FROM Launchings
WHERE ID = 210423
)
SELECT Date,
[Start1] = MIN(CASE WHEN RowNum = 1 THEN Start END),
[End1] = MIN(CASE WHEN RowNum = 1 THEN [End] END)
[Start2] = MIN(CASE WHEN RowNum > 1 THEN Start END),
[End2] = MAX(CASE WHEN RowNum > 1 THEN [End] END)
FROM RankedData
GROUP BY Date;`

Using a sub query in column list

I would like to create a query that would count how many records were created in the last 7, 14 and 28 days. My result would return something like:
7Days 14Days 28Days
21 35 56
I know how to for each timepsan e.g. 7 days, but I do I capture all three in one query?
select count(*) from Mytable
where Created > DATEADD(day,-8, getdate())
Also not pretty, but doesn't rely on subqueries (table/column names are from AdventureWorks). The case statement returns 1 if it falls within your criteria, 0 otherwise - then you just sum the results :
select sum(case when datediff(day, modifieddate, getdate()) <= 7
then 1 else 0 end) as '7days',
sum(case when datediff(day, modifieddate, getdate()) > 7
and datediff(day, modifieddate, getdate()) <= 14
then 1 else 0 end) as '14days',
sum(case when datediff(day, modifieddate, getdate()) > 14
and datediff(day, modifieddate, getdate()) <= 28
then 1 else 0 end) as '28days'
from sales.salesorderdetail
Edit: Updated the datediff function - the way it was written, it would return a negative number (assuming modifieddate was in the past) causing all items to fall under the first case. Thanks to Andriy M for pointing that out
It isn't the prettiest code in the world, but it does the trick. Try selecting from three subqueries, one for each range.
select * from
(select COUNT(*) as Cnt from Log_UrlRewrites where CreateDate >= DATEADD(day, -7, getdate())) as Seven
inner join (select COUNT(*) as Cnt from Log_UrlRewrites where CreateDate >= DATEADD(day, -14, getdate())) as fourteen on 1 = 1
inner join (select COUNT(*) as Cnt from Log_UrlRewrites where CreateDate >= DATEADD(day, -28, getdate())) as twentyeight on 1 = 1
select
(
select count(*)
from Mytable
where Created > DATEADD(day,-8, getdate())
) as [7Days],
(
select count(*)
from Mytable
where Created > DATEADD(day,-15, getdate())
) as [14Days],
(
select count(*)
from Mytable
where Created > DATEADD(day,-29, getdate())
) as [28Days]
SELECT
[7Days] = COUNT(CASE UtmostRange WHEN 7 THEN 1 END),
[14Days] = COUNT(CASE UtmostRange WHEN 14 THEN 1 END),
[28Days] = COUNT(CASE UtmostRange WHEN 28 THEN 1 END)
FROM (
SELECT
*,
UtmostRange = CASE
WHEN Created > DATEADD(day, -8, GETDATE()) THEN 7
WHEN Created > DATEADD(day, -15, GETDATE()) THEN 14
WHEN Created > DATEADD(day, -29, GETDATE()) THEN 28
END
FROM Mytable
) s