How to use recurring CTE for Time Series - sql

I have a table which shows the following data
Called Declines
date
accountId
declineReason
04/10/22
1344
Not enough funds
05/10/22
1222
Incorrect Password
05/10/22
1677
timeout
06/10/22
1222
Incorrect Password
07/10/22
1677
timeout
07/10/22
1222
Incorrect Password
10/10/22
1677
timeout
11/10/22
1344
Incorrect Password
11/10/22
1222
Incorrect Password
12/10/22
1677
timeout
13/10/22
1222
Incorrect Password
15/10/22
1677
timeout
15/10/22
1222
Incorrect Password
17/10/22
1677
timeout
etc (note declineReasons may increase in terms of distinct values and is not limited to 3 shown)
what I would like to have as my final table is the below
WeekEnd
declineReason
CountOfReasonPerWeek
10/10/22
Not enough funds
1
10/10/22
Incorrect Password
3
10/10/22
timeout
3
17/10/22
Not enough funds
0
17/10/22
Incorrect Password
4
17/10/22
timeout
3
I have created the week loop and table which counts no. of total declines
WITH date_loop AS (
SELECT
CAST('2022-08-01' AS DATE) AS WeekEnd,
CAST('2022-08-02' AS DATE) AS BeforeDate,
CAST('2022-07-25' AS DATE) AS AfterDate
UNION ALL
SELECT
DATEADD(ww, 1, WeekEnd),
DATEADD(ww, 1, BeforeDate),
DATEADD(ww, 1, AfterDate)
FROM date_loop
WHERE BeforeDate < DATEADD(ww, -1, GETDATE())
),
CountOfDDeclineByReasonAllTime AS (
SELECT
count(*) AS totalDeclinesPerReasonCode
FROM
Declines
GROUP BY
declineReason),
But I am unsure how I can use a correlated subquery to get values returned in the format as desired, if anyone has any pointers that would be great

data
CREATE TABLE Declines(
date VARCHAR(100) NOT NULL
,accountId INTEGER NOT NULL
,declineReason VARCHAR(100) NOT NULL
);
INSERT INTO Declines
(date,accountId,declineReason) VALUES
('04/10/22',1344,'Not enough funds'),
('05/10/22',1222,'Incorrect Password'),
('05/10/22',1677,'timeout'),
('06/10/22',1222,'Incorrect Password'),
('07/10/22',1677,'timeout'),
('07/10/22',1222,'Incorrect Password'),
('10/10/22',1677,'timeout'),
('11/10/22',1344,'Incorrect Password'),
('11/10/22',1222,'Incorrect Password'),
('12/10/22',1677,'timeout'),
('13/10/22',1222,'Incorrect Password'),
('15/10/22',1677,'timeout'),
('15/10/22',1222,'Incorrect Password'),
('17/10/22',1677,'timeout');
query
first since your value is not date change to date by using Convert(date, column, 3)
second get the last day of week by using
DATEADD(dd, 7-(DATEPART(dw, column)), column) as WeekEnd
third use Subquery to have better understanding of using Count function
select WeekEnd,
       declineReason,
       Count(declineReason) CountOfReasonPerWeek
from   (select Dateadd(dd, 7 - ( Datepart(dw, Convert(date, date, 3)) ),
                      Convert(date, date, 3)
               ) WeekEnd,
               declineReason
        from   Declines) a
group  by declineReason,
          WeekEnd
dbfiddle

Related

LAG and LEAD based on parameter

I have table - Invoices, with such structure:
Id
InvoiceNo
Date
1
10
11-12-21
2
20
12-12-21
3
30
13-12-21
4
40
NULL
5
50
14-12-21
6
60
NULL
7
70
NULL
8
80
15-12-21
What I need to do - I need to find InvoiceNo's, the date field of the next or previous line of which contains null.
So, based on provided data - I should receive:
InvoiceNo
30
50
80
But how to do this? One option that I found - LAG() and LEAD() functions, and with these functions I can receive numbers and dates, but cannot use parameters - so cannot provide "Date is not null" check.
You can use lag and lead to find the previous and next rows, and then wrap the query with another query that returns only the rows where one of them was null. Note that lag of the first row and lead of the last raw will return null by default, so you need to explicitly state a non-null default, such as getdate():
SELECT InvoiceNo
FROM (SELECT InvoiceNo,
LAG(Date, 1, GETDATE()) OVER (ORDER BY InvoiceNo) AS lag_date,
LEAD(Date, 1, GETDATE()) OVER (ORDER BY InvoiceNo) AS lead_date
FROM invoices) t
WHERE lag_date IS NULL OR lead_date IS NULL

Selecting records younger then 2 minutes with DATEDIFF

I'm currently facing the following problem:
I want to select all the records from my database table LocationUpdates for a specific RFIDTagID AND a ScannedTime smaller then 2 min compared to the current time.
I am giving sql two parameters: 1. RFIDTagID (Needed to select only results in the database with this tagID, 2. ScannedTime (CurrentTimeStamp).
Now I want to ask the databse: Give me all records for RFIDTagID 123456789 where the ScannedTime max 2 min earlier is than the second parameter ScannedTime.
When SQL returns results: Than don't add the row.
Whenm SQL doesn't return results: Than you have to add the row.
I am making a stored procedure to perform this task. It looks like the following:
CREATE PROCEDURE
SELECT COUNT(*) FROM dbo.LocationUpdates updates WHERE updates.RFIDTagID = #RFIDTagID AND DATEDIFF(MINUTE, #ScannedTime, updates.ScannedTime) < 2
IF ##ROWCOUNT = 0 THEN PERFORM SOME TASKS AND ADD THE ROW
ELSE DO NOTHING
I have the following data in my database:
160 300833B2DDD9014035050005 18-7-2013 11:18:44
161 300833B2DDD9014035050005 18-7-2013 11:19:50
162 300833B2DDD9014035050005 18-7-2013 11:24:03
163 300833B2DDD9014035050005 18-7-2013 13:38:50
164 300833B2DDD9014035050005 18-7-2013 13:39:29
165 300833B2DDD9014035050005 1-1-1900 0:00:00
AND When I execute the following query (With the currentdate):
DECLARE #return_value Int
DECLARE #currDate DATETIME
SET #currDate = GETDATE()
EXEC #return_value = [dbo].[INSERT_LOCALROW]
#RFIDTagID = N'300833B2DDD9014035050005',
#ScannedTime = #currDate
SELECT 'Return Value' = #return_value
GO
This query returns the following result: 6 rows
But I am expecting to get 0 rows in return as none of the result is two minutes different then the current time.
Someone has any suggestions?
EDIT
I have found already the answer:
SELECT COUNT(*) FROM dbo.LocationUpdates updates WHERE updates.RFIDTagID = #RFIDTagID AND DATEDIFF(MINUTE, #ScannedTime, updates.ScannedTime) > -2
The function DateDiff gives a negative int when you compare a newer date with an older date, so it will return -2 when the time in the database is two minutes earlier then the current time.
I would replace
DATEDIFF(MINUTE, #ScannedTime, updates.ScannedTime) < 2
Cause if the second argument is bigger than the third argument (the dates), you will have a negative result. And... a negative result is smaller than 2.
by
updates.ScannedTime > DATEADD(MINUTE, -2, #ScannedTime)
or invert parameters
DATEDIFF(MINUTE, updates.ScannedTime, #ScannedTime) < 2

Find rows in a database with no time in a datetime column

During testing I have failed to notice an incorrect date/time entry into the database on certain orders. Instead of entering the date and time I have only been entering the date. I was using the correct time stamp createodbcdatetime(now()) however I was using cfsqltype="cf_sql_date" to enter it into the database.
I am lucky enough to have the order date/time correctly recorded, meaning I can use the time from the order date/time field.
My question being can I filter for all rows in the table with only dates entered. My data below;
Table Name: tbl_orders
uid_orders dte_order_stamp
2000 02/07/2012 03:02:52
2001 03/07/2012 01:24:21
2002 03/07/2012 08:34:00
Table Name: tbl_payments
uid_payment dte_pay_paydate uid_pay_orderid
1234 02/07/2012 03:02:52 2000
1235 03/07/2012 2001
1236 03/07/2012 2002
I need to be able to select all payments with no time entered from tbl_payments, i can then loop around the results grabbing the time from my order table add it to the date from my payment table and update the field with the new date/time.
I can pretty much handle the re-inserting the date/time. It's just selecting the no time rows I'm not sure about?
Any help would be appreciated.
The following is the select statements for both orders and payments and if they need to be joined.(just fyi)
SQL Server 2008, Cold Fusion 9
SELECT
dbo.tbl_orders.uid_orders,
dbo.tbl_orders.dte_order_stamp,
dbo.tbl_payment.dte_pay_paydate,
dbo.tbl_payment.uid_pay_orderid
FROM
dbo.tbl_orders
INNER JOIN dbo.tbl_payment ON (dbo.tbl_orders.uid_orders = dbo.tbl_payment.uid_pay_orderid)
SELECT
dbo.tbl_orders.uid_orders,
dbo.tbl_orders.dte_order_stamp
FROM dbo.tbl_orders
SELECT
uid_paymentid,
uid_pay_orderid,
dte_pay_paydate,
FROM
dbo.tbl_payment
Select the records where the hours, minutes, seconds and millisecond value is zero.
select *
from table
where datePart(hour, datecolumn) = 0
and datePart(minute, datecolumn) = 0
and datePart(second, datecolumn) = 0
and datePart(millisecond, datecolumn) = 0
You can probably get those values by casting to time and checking for 0:
SELECT * FROM table WHERE CAST(datetimecolumn AS TIME) = '00:00'
That may not be particularly efficient though, depending on how smart SQL Server's indexes are.
Something like this should work:
....
WHERE CAST(CONVERT(VARCHAR, dbo.tbl_payment.dte_pay_paydate, 101) AS DATETIME) =
dbo.tbl_payment.dte_pay_paydate
This will return all rows where the time is missing.

copy first cell to second then add hours as int (DATEPART)

I have data in the table like the following.
TimeIn TimeOut
-------------------------------------
6/1/2010 09:00:00 Null
6/2/2010 09:00:00 6/2/2010 16:45:00
6/3/2010 10:05:00 Null
6/4/2010 07:30:00 6/4/2010 15:45:00
i have the stored procedure to find last activity with "not signd out" column
i have a stored procedure for copy a cell to other too ...
then, what i need, is to update [TimeOut] (if there's no time out)
via more elegant way like
UPDATE TimeOut SET DATEPART(HOUR, TimeOut) = DATEPART(HOUR, TimeIN) + 8
so the Whole idea was to
first check if last activity - Timeout Column is null
then if it is, sign TimeOut with max work hours allowed (8).
is there a simple way to do it ?
UPDATE
as to marc answer , this is the selection of find out if user didn't sign out
SELECT CASE WHEN [TimeOut] IS NULL THEN '' ELSE CONVERT(NVARCHAR,[TimeOut]) END FROM tblTime WHERE tId = ( SELECT MAX(tId) FROM tblTime WHERE UserId = 123
so i have the query that finds who did not sign out at last activity
then i only need to update that specific Row - field TimeOut
with hours of time in + 8
that was my question
Looks like you should use dateadd.
UPDATE TimeOut
SET TimeOut = DATEADD(HOUR, 8, TimeIN)
WHERE ....
This will set TimeOut to TimeIn plus eight hours.
add a where TimeOut IS NULL clause so it updates only the ones where the field actually is null?
UPDATE tblTime SET TimeOut = DATEADD(HOUR,8,TimeIn) WHERE tId = ( SELECT MAX(tId) FROM tblTime WHERE UserId = 1234)

Aggregate SQL column values by time period

I have some numerical data that comes in every 5 minutes (i.e. 288 values per day, and quite a few days worth of data). I need to write a query that can return the sums of all values for each day. So currently the table looks like this:
03/30/2010 00:01:00 -- 553
03/30/2010 00:06:00 -- 558
03/30/2010 00:11:00 -- 565
03/30/2010 00:16:00 -- 565
03/30/2010 00:21:00 -- 558
03/30/2010 00:26:00 -- 566
03/30/2010 00:31:00 -- 553
...
And this goes on for 'x' number of days, I'd like the query to return 'x' number of rows, each of which containing the sum of all the values on each day. Something like this:
03/30/2010 -- <sum>
03/31/2010 -- <sum>
04/01/2010 -- <sum>
The query will go inside a Dundas webpart, so unfortunately I can't write custom user functions to assist it. All the logic needs to be in just the one big query. Any help would be appreciated, thanks. I'm trying to get it to work using GROUP BY and DATEPART at the moment, not sure if it's the right way to go about it.
U can use CAST to date type
SELECT [ENTRY_DATE],CAST([ENTRY_DATE] AS date) AS 'date'
FROM [PROFIT_LIST]
Now you can group by according to this.
SELECT CAST([ENTRY_DATE] AS date) AS 'date',SUM(PROFIT)
FROM [PROFIT_LIST]
GROUP BY CAST([ENTRY_DATE] AS date) AS 'date'
Here's a nice trick. If you cast a SQL DATETIME to a FLOAT it gives you the date as days.fractionofday
Therefore if you floor that, and turn it back to a DATETIME it gives you minight on the given date.
CAST(FLOOR(CAST(MyDateTime AS FLOAT)) AS DATETIME)
Therefore, my favourite way of doing this is.
select
CAST(FLOOR(CAST(OrderDate AS FLOAT)) AS DATETIME)
, sum(taxamt) as Amount
from
Sales.SalesOrderHeader
group by
CAST(FLOOR(CAST(OrderDate AS FLOAT)) AS DATETIME)
I have no idea if that is more/less eficient than any previous correct answers.
I do not see how you could use DATEPART for this since it cannot return only the date part of a DATETIME value (correct me if I am mistaken). What does work is use DATEADD to "null" the time and group by a value of the form YYYY-MM-DD 00:00:00.000.
The following query works against the Adventure Works database in case you happen to have it (tested on SQL Server 2005). Other than using DATEADD it is very similar to #OMG Ponies suggestion:
select
dateadd(dd, 0, datediff(dd, 0, OrderDate)) as SaleDay
, sum(taxamt) as Amount
from
Sales.SalesOrderHeader
group by
dateadd(dd, 0, datediff(dd, 0, OrderDate))
order by
SaleDay
The idea of dateadd(dd, 0, datediff(dd, 0, OrderDate)) is to first get the "number of days passed from the beginning of time until your date" (the datediff-part) and then add this number of days to "the beginning of time". Which gives you the "start of your day". I hope this is understandable :)