Looping distinct values from one table through another without a join - sql

I know looping is not ideal in SQL, but I couldn't think of another way of doing this.
I want each distinct row from this Table 1 to have each distinct date and hour produced on Table 2.
In other words, Table 2 has the dates between 05/01/2014 through 04/30/2015, with each distinct date having 24 rows, one for each hour of the day. I now want each distinct row in Table 1 to have each distinct date on Table 2, with each of its 24 hours.
Table 1:
DROP TABLE Baylor_Raw..MEDICAL_SERVICE_DESC
SELECT MEDICAL_SERVICE_DESC
INTO Baylor_Raw..MEDICAL_SERVICE_DESC
FROM Baylor_Raw..Raw_ADT
WHERE MEDICAL_SERVICE_DESC IS NOT NULL AND MEDICAL_SERVICE_DESC NOT IN ('#N/A','CANCEL','DAY SURGERY','HOSPICE','INFUSION')
Table 2:
DECLARE #DATE DATE
SET #DATE = '05/01/2014'
DECLARE #HOUR INT
SET #HOUR = 0
DROP TABLE Baylor_Raw..DateTable
CREATE TABLE Baylor_Raw..DateTable
(DATE_OF_DISCHARGE DATE
,HOUR_OF_DISCHARGE INT)
WHILE #DATE<'05/01/2015' BEGIN
WHILE #HOUR<25 BEGIN
INSERT INTO Baylor_Raw..DateTable (DATE_OF_DISCHARGE,HOUR_OF_DISCHARGE)
VALUES (#DATE,#HOUR)
SET #HOUR = #HOUR+1
END
SET #DATE = DATEADD(DD,1,#DATE)
SET #HOUR = 0
END
This below attempt did not work. I canceled after the run time exceeded several minutes.
DECLARE #MEDICAL_SERVICE_DESC NVARCHAR(255)
SET #MEDICAL_SERVICE_DESC = (SELECT MIN(MEDICAL_SERVICE_DESC) FROM Baylor_Raw..MEDICAL_SERVICE_DESC)
DECLARE #DATE DATE
SET #DATE = '05/01/2014'
DECLARE #HOUR INT
SET #HOUR = 0
DROP TABLE Baylor_Raw..DateTable
CREATE TABLE Baylor_Raw..DateTable
(MEDICAL_SERVICE_DESC NVARCHAR,
DATE_OF_DISCHARGE DATE
,HOUR_OF_DISCHARGE INT)
WHILE #MEDICAL_SERVICE_DESC IS NOT NULL BEGIN
WHILE #DATE<'05/01/2015' BEGIN
WHILE #HOUR<25 BEGIN
INSERT INTO Baylor_Raw..DateTable (MEDICAL_SERVICE_DESC,DATE_OF_DISCHARGE,HOUR_OF_DISCHARGE)
VALUES (#MEDICAL_SERVICE_DESC,#DATE,#HOUR)
SET #HOUR = #HOUR+1
END
SET #DATE = DATEADD(DD,1,#DATE)
SET #HOUR = 0
END
DELETE FROM Baylor_Raw..MEDICAL_SERVICE_DESC WHERE MEDICAL_SERVICE_DESC = #MEDICAL_SERVICE_DESC
SET #MEDICAL_SERVICE_DESC = (SELECT MIN(MEDICAL_SERVICE_DESC) FROM Baylor_Raw..MEDICAL_SERVICE_DESC)
SET #DATE = '05/01/2014'
END

So you want all distinct records from table1 paired with all records in table2? That is a cross join:
select *
from (select distinct * from table1) t1
cross join table2;
Or do you want them related by date? Then inner-join:
select *
from (select distinct * from table1) t1
inner join table2 t2 on t1.date = t2.date;

You might get better performance from building lists of dates and hours using loops first, and then combining it with the service names just one time using a cross join, something like this. You may need to tweak it a bit because you understand the source data:
DECLARE #dates TABLE (DATE DATE)
DECLARE #hours TABLE (hours INT)
DECLARE #hour INT
,#date DATE
SET #hour = 0
SET #date = '05/01/2014'
WHILE (#hour < 25)
BEGIN
INSERT INTO #hours
SELECT #hour
SET #hour = #hour + 1
END
WHILE (# DATE < '05/01/2015')
BEGIN
INSERT INTO #dates
SELECT #date
SET #DATE = DATEADD(DD, 1, #DATE)
END
INSERT INTO Baylor_Raw..DateTable (
MEDICAL_SERVICE_DESC
,DATE_OF_DISCHARGE
,HOUR_OF_DISCHARGE
)
SELECT MEDICAL_SERVICE_DESC
,DATE
,hour
FROM Baylor_Raw..MEDICAL_SERVICE_DESC
CROSS JOIN #date d
CROSS JOIN #hours h

Related

SQL query to check availability on an event table

I have a table of events with the columns Room Number and Date
I want to check availability in a certain room so I tried creating a temp calendar table just to compare, but I know I'm doing something wrong with my left join so I need some pointers to help me achieve my goal: only show the dates where there is no event in the specific room.
This is my query so far:
IF OBJECT_ID('tempdb..#calendarTable') IS NOT NULL
DROP TABLE #calendarTable;
GO
CREATE TABLE #calendarTable (
"DayID" INT
,"DayDate" DATE
,PRIMARY KEY (DayID)
)
DECLARE #startDate AS DATE
DECLARE #baseDate DATE
,#offSet INT
,#numberOfLoops INT
SET #startDate = GETDATE() /*'20150101'*/
SET #baseDate = #startDate
SET #offSet = 0
SET #numberOfLoops = DATEDIFF(DAY, #baseDate, DATEADD(MONTH, 24, #baseDate)) /*365*/
WHILE (#offSet < #numberOfLoops)
BEGIN
INSERT INTO #calendarTable (
"DayID"
,"DayDate"
)
SELECT (#offSet + 1)
,DATEADD(DAY, #offSet, #baseDate)
SET #offSet = #offSet + 1
END
SELECT TOP (500)
DayDate
,a.Arrangement_Number
,a.Activity
,a.Room_Number
FROM #calendarTable
left outer join [Events] as a ON DayDate = (convert(varchar(10), a.[Date],120))
where Room_Number = 53
order by DayDate asc

SQL Query, bring data between 2 dates with limitation

In SQL, I am going to write a query which insert all data between 2 dates and also I want to bring then in a 1000 batch but since the number of data between those days are more than my limitation I was going to write a loop which makes the smaller period to bring the data.
here is my code:
DECLARE #StartDate DATETIME = CAST('2021-06-02 01:00:00.000' AS DATETIME)
DECLARE #EndDate DATETIME = CAST('2021-06-23 01:00:00.000' AS DATETIME)
DECLARE #RealRowCount INT = (SELECT DISTINCT SUM(##ROWCOUNT) OVER() FROM GetReport (
#StartDate, #EndDate))
DECLARE #TransactionCount INT = (SELECT DISTINCT TransactionCount FROM GetReport (
#StartDate, #EndDate))
WHILE #RealRowCount < #TransactionCount
BEGIN
DECLARE #DiffDate INT = (SELECT DATEDIFF(DAY, #StartDate, #EndDate))
SET #EndDate = DATEADD(DAY, #DiffDate/2 ,#StartDate)
SELECT *,#StartDate, #EndDate FROM GetReport (#StartDate, #EndDate)
END
PS: I was thinking about find the middle of the period of date and then change them into the new EneDate and StartDate but there is problem here!
Your question is not very clear. Suppose you have 10,000 records between two dates and you do not want to retrieve more than a thousand records at a time. In this case, you can use pagination. Both in the program code and in SQL.
DECLARE #StartDate DATETIME = CAST('2021-06-02 01:00:00.000' AS DATETIME)
DECLARE #EndDate DATETIME = CAST('2021-06-23 01:00:00.000' AS DATETIME)
DECLARE #RealRowCount INT = (SELECT DISTINCT COUNT(*) FROM Products WHERE InsertDate BETWEEN #StartDate AND #EndDate)
DECLARE #Counter INT = 0
WHILE #Counter <= #RealRowCount
BEGIN
SELECT *
FROM Products
WHERE InsertDate BETWEEN #StartDate AND #EndDate
ORDER BY InsertDate
OFFSET #Counter ROWS -- skip #Counter rows
FETCH NEXT 1000 ROWS ONLY -- take 1000 rows
SET #Counter = #Counter + 1000
END
Or you can get the time difference between the two dates and add the start date each time in a specific step and retrieve the data of that date.
For example, the date difference is 20 days. Increase the start date by 5 steps each time to the start date with the end date
I create another table to put Dates and if this table has any rows I can get 'EndDate', but if it has not any records I simply just use the date that I specified.
AccSync is the table that I insert details of my records and AccTransformation is the table wich I want to insert all of my records.
DECLARE #Count INT = (SELECT COUNT(*) FROM [AccTransaction])
DECLARE #Flag BIT = (SELECT IIF(#Count > 1, 1, 0))
DECLARE #End DATETIME = GETDATE();
DECLARE #Start DATETIME
IF(#Flag = 0)
BEGIN
SET #Start = CAST('2021-03-08' AS DATETIME2);
SET #Flag = 1
END
ELSE IF(#Flag = 1)
BEGIN
SET #Start = (SELECT TOP 1 EndDate FROM (SELECT EndDate FROM [AccSync] ORDER BY ActionDate DESC OFFSET 0 ROW) AS TT);
END
DECLARE #RealRowCount INT = (SELECT DISTINCT SUM(##ROWCOUNT) FROM [GetReport] (#Start, #End));
DECLARE #TransactionCount INT = (SELECT DISTINCT TransactionCount FROM [GetReport] (#Start, #End));
----------------------------------------------------------------------------------------------
WHILE (#RealRowCount <> #TransactionCount)
BEGIN
DECLARE #DiffDate INT = (SELECT DATEDIFF(SECOND, #Start, #End))
SET #End = DATEADD(SECOND, (#DiffDate/2), #Start)
SET #RealRowCount = (SELECT DISTINCT SUM(##ROWCOUNT) FROM [GetReport] (#Start, #End))
SET #TransactionCount = (SELECT DISTINCT TransactionCount FROM [GetReport] (#Start, #End))
END
----------------------------------------------------------------------------------------------
INSERT INTO [AccTransaction]
SELECT *
FROM [GetReport](#Start, #End)
----------------------------------------------------------------------------------------------
INSERT INTO [AccSync]
VALUES(NEWID(), GETDATE(), #Start, #End, ISNULL(#TransactionCount,0), DATEDIFF(SECOND, #Start, #End))

Generate random records for datetime columns by stored procedure in SQL

I want to generate 5 random records from a field which is a datetime column and contains several records of (OrderDate) for a given date range using stored procedure for the table named Orders
CREATE PROCEDURE test
#StartDate DATETIME = NULL,
#EndDate DATETIME = NULL,
AS
BEGIN
SELECT OrderDate = DATEADD(......)
FROM Orders
END
May I get some help!
A while loop works ok for this purpose, especially if you're concerned with limiting your randomness to a bounded date range.
The downside is that potentially many insert queries get executed vs. a single insert for a recursive CTE as in the other answer.
create procedure dbo.spGenDates2
#MinDate datetime,
#MaxDate datetime,
#RecordCount int = 5
as
SET NOCOUNT ON;
DECLARE #Range int, #DayOffset int, #Cnt int
SET #Range = DATEDIFF(dd, #MinDate, #MaxDate)
SET #Cnt = 1
WHILE #Cnt <= #RecordCount
BEGIN
SET #DayOffset = RAND() * (#Range + 1)
INSERT INTO _test (Dt) VALUES(DATEADD(dd, #DayOffset, #MinDate))
SET #Cnt = #Cnt + 1
END
Based on your syntax I'm assuming you're using SQL Server...
Note that you cannot reliably use the sql random number generator function RAND() within the context of a single query because it does not get reseeded per row so you end up receiving the same, single random number for each row result. Instead, an approach using NEWID() converted into a numeric does the trick when generating random values within the execution of a single query.
Here's a procedure that will give you n number of sample dates in the near past.
create procedure dbo.spGenDates
#MaxDate datetime,
#RecordCount int = 5
as
WITH dates as (
SELECT DATEADD(MILLISECOND, ABS(CHECKSUM(NEWID())) * -1, #MaxDate) D,
1 as Cnt
UNION ALL
SELECT DATEADD(MILLISECOND, ABS(CHECKSUM(NEWID())) * -1, #MaxDate) D,
x.Cnt + 1 as Cnt
FROM dates x
WHERE x.Cnt < #RecordCount
)
INSERT INTO _test (Dt)
SELECT D
FROM dates
The wording of the question has been clarified (see comments on another answer) to be a desire to SELECT 5 random sample dates within a bounded range from a table.
A query like this will yield the desired result.
SELECT TOP (5) OrderDate
FROM Orders
WHERE OrderDate >= #StartDate
AND OrderDate < #EndDate
ORDER BY NEWID()

Not able to execute the stored procedure

I have table in which my date column value is saved with time also
like this 2016-06-10 14:56:11.000
Now while executing my SP, I pass one parameter as date like this exec UserReportData '06-10-2016' but it is not showing any records. As it has 4 records in the table.
Why?
UPDATE
ALTER PROCEDURE [dbo].[UserReportData]
#As_ONDATE Datetime
AS
BEGIN
DECLARE #REPORTDATE datetime
DECLARE #OPENING INT
SELECT *
INTO #temptable
FROM
(SELECT DISTINCT
a.CUser_id, b.User_Id,a.U_datetime as REPORTDATE,
b.first_name + ' ' + b.last_name AS USERNAME,
0 OPENING, 0 TOTAL_DOCUMENT, 0 INWARD, 0 FIRST_LEVEL_PROCESSING, 0 DATA_ENTRY
FROM
inward_doc_tracking_trl a, user_mst b
WHERE
a.CUser_id = b.mkey
AND a.U_datetime = CONVERT(varchar(10), #As_ONDATE, 103)) AS x
DECLARE Cur_1 CURSOR FOR
SELECT CUser_id, User_Id
FROM #temptable
OPEN Cur_1
DECLARE #CUser_id INT
DECLARE #User_Id INT
FETCH NEXT FROM Cur_1 INTO #CUser_id, #User_Id
WHILE (##FETCH_STATUS = 0)
BEGIN
SELECT #REPORTDATE
FROM inward_doc_tracking_trl
WHERE U_datetime = CONVERT(varchar(10), #As_ONDATE, 103)
UPDATE #temptable
SET REPORTDATE = #REPORTDATE
WHERE CUser_id = #CUser_id
AND User_Id = #User_Id
FETCH NEXT FROM Cur_1 INTO #CUser_id, #User_Id
END
CLOSE Cur_1
DEALLOCATE Cur_1
SELECT *
FROM #temptable
DROP TABLE #temptable
END
You are passing in a date as a string (with implicit time being 00:00) which you are casting to be a date, still with time being 00:00, and trying to match dates with times. There won't be any results as the time doesn't match.
You have to either:
Cast the datetime to a date to match an exact date (not good, requires recalculating every date in the column)
Change the search to look between date + '00:00' to date + '23:59' (or if you are happy, you could just add a day)
Update for your where clause to take the easy option 2:
where a.CUser_id = b.mkey
and a.U_datetime BETWEEN CONVERT(varchar(10), #As_ONDATE, 103)
AND DATEADD(day, 1, CONVERT(varchar(10), #As_ONDATE, 103))

Autofill SQL table field with Datetime value based on input parameter

I have a datetime field in my SQL table. I have created a procedure that takes count as a variable and generates records for the table. If the count is 5 it will generate 5 records.The logic i want is that when i provide 5 as an input parameter the datetime field in the table should be autofilled with values
12/20/2015 9:00
12/20/2015 11:00
12/20/2015 13:00
12/20/2015 15:00
12/20/2015 17:00
So every time a record is inserted into a table,the 2 hours of time should be added.
Recursive CTEs are one way to create records on the fly. The key here is to create an anchor (this is the first SELECT inside the CTE, which is your starting point). And an exit check (which is the WHERE clause).
Read up on MAXRECURSION if you want to create more than 100 records at a time.
Example
DECLARE #RecordsRequired INT = 5;
DECLARE #BaseDateTime SMALLDATETIME = GETDATE();
WITH [Sample] AS
(
/* This CTE uses recursion to create the required number of
* records.
*/
SELECT
1 AS RowNumber,
#BaseDateTime AS [DateTime]
UNION ALL
SELECT
RowNumber + 1 AS RowNumber,
DATEADD(HOUR, 2, [DateTime]) AS [DateTime]
FROM
[Sample]
WHERE
RowNumber < #RecordsRequired
)
SELECT
RowNumber,
[DateTime]
FROM
[Sample]
;
You could also look into WHILE blocks.
Use this code:
------------------ INPUT ------------------------
declare #start_date datetime = '01/01/2000 14:00'
declare #loops int = 5
-------------------------------------------------
declare #i int = 0
while (#i < #loops) begin
select dateadd(hour, #i * 2, #start_date)
set #i = #i + 1
end
Try this without LOOP
Declare #count int = 5,
#incrementer int =2 -- in case if you want to change the incrementer
SELECT Dateadd(hh, num * #incrementer, dates)
FROM (SELECT Cast(CONVERT(VARCHAR(20), Dateadd(dd, 1, Getdate()), 111)
+ ' 9:00 AM' AS DATETIME) AS Dates,
num
FROM (VALUES(0),(1),(2),(3),(4),(5)) TC (num)) A
WHERE num <= #count - 1
SQL FIDDLE DEMO
Please find the sample code below, it contains the logic that you needed. Hope it helps!!
--Create a temp table for sample output
CREATE TABLE #temp
(
CreatedDate datetime
)
--Declaring variables
DECLARE #Count int
DECLARE #TimeCounter int
--intializing values
SET #Count=5
SET #TimeCounter=0
WHILE(#Count>0)
BEGIN
--SELECT getdate()+1
insert into #temp(#temp.CreatedDate) Select DATEADD(hour,#TimeCounter,getdate())
SET #TimeCounter=#TimeCounter+2
SET #Count=#Count-1
END
--Final values
SELECT * FROM #temp tmp
--Dropping table
DROP TABLE #temp
This is one of those problems that's best solved with a numbers table / function. Much less code than recursion or loops, usually faster for anything non-trivial and more reusable too.
The core code you want is
CREATE PROCEDURE usp_PopulateAppointments
(
#StartDateTime datetime2(3),
#Records int,
#Interval int = 120 --Time between appointment slots in minutes. Default to 2h if not manually specified.
)
INSERT INTO Appointments
SELECT
DATEADD(m, #Interval * Number, #StartDateTime)
FROM dbo.udfNumbers(0, #Recs)
I've assumed in this a numbers function that takes #StartAt and #NumberResults. I use one derived from Adam's final code in the comments on http://dataeducation.com/you-require-a-numbers-table/ - in my experience it's faster than a real table, and takes less space too.
Create Table dates
(
datetimefield datetime not null
)
go
Create Procedure FillDateTimeField
#insertxrows int
AS
begin
Declare #LastDateTimeInserted as datetime
set #LastDateTimeInserted = (select isnull(max(datetimefield),getdate()) from Dates)
;WITH norows AS (
SELECT 1 as num, Dateadd(hour,2,#LastDateTimeInserted) as FirstRecord
UNION ALL
SELECT num + 1, dateadd(hour,2,firstrecord) FROM
norows
WHERE num < #insertxrows
)
insert into dates
select firstrecord from norows
end