Incrementing a variable inside a looped INSERT in SQL Server 2012 - sql

How can we be able to include the other column to loop the same as the looped month in this code?
What I'm trying to achieve is the range of $datestart and $dateend should loop the [Data 3] as well.
If [Data 3] is equal to 100, and the range of $datestart and $dateend is 10
Data 3 column should increment from 100,101,102,103,104,105.. and so on up to 110
DECLARE #MYTESTTABLE TABLE
(
[Month] DATE,
[Data 1] INT,
[Data 2] INT,
[Data 3] INT
);
WITH MYLOOP AS
(
SELECT CAST('$datestart' AS DATE) AS [date], '$datavalue' AS [Data 3]
UNION ALL
SELECT DATEADD(MONTH, 1, [date]), [Data 3] = [Data 3] + 1
FROM MYLOOP
WHERE DATEADD(MONTH, 1, [date]) < CAST('$dateend' AS DATE)
)
INSERT INTO #MYTESTTABLE ([Month], [Data 1], [Data 2], [Data 3])
SELECT
LEFT([date], 10) AS [Month], 100 AS [Data 1], 100 AS [Data 2], [Data 3]
FROM
MYLOOP
SELECT *
FROM #MYTESTTABLE
Suppose $datavalue is equal to 100, and it will loop along with the range of the loop from $datestart to $dateend.
Example
The next column (Data 3) should be 100,101,102,103,104,... and so on
How can we achieve this?
I tried doing it like this [Data 3] = [Data 3] + 1 but I get this error:
Types don't match between the anchor and the recursive part in column "Data 3" of recursive query "MYLOOP".

The error message just happens to be the first error that SQL Server picks. There is in fact a great deal wrong with your SQL that SQL Server is not reporting.
Firstly T-SQL uses #variablename to denote variables (not $variablename). Secondly enclosing something in single quotes (') will cause SQL Server to treat it as a literal string. If you enter the following in a Query window:
SELECT CAST('$datestart' as date)
you will get a conversion failed error.
In addition I strongly recommend that you strongly type your variables #datestart and #dateend so that you do not need to use casts.
As an example try the following:
DECLARE #datestart date = '2019-01-01'
DECLARE #dateend date = '2019-11-02'
DECLARE #datavalue int = 100
DECLARE #MYTESTTABLE TABLE ([Month] date, [Data 1] INT, [Data 2] INT, [Data 3] INT);
;WITH MYLOOP AS (
SELECT #datestart AS [date], #datavalue as [Data 3]
UNION ALL
SELECT DATEADD(MONTH, 1, [date]), [Data 3]=[Data 3]+1
FROM MYLOOP
WHERE DATEADD(MONTH, 1, [date]) < #dateend)
INSERT INTO #MYTESTTABLE ([Month], [Data 1], [Data 2], [Data 3])
SELECT [date] AS [Month], 100 AS [Data 1], 100 AS [Data 2], [Data 3]
FROM MYLOOP;
SELECT * FROM #MYTESTTABLE
This will give you data from 2019-01-01 to 2019-11-01. Note I set #dateend as 2019-11-02, because you are using < #dateend. This ensures that you will also get the desired last date. Otherwise you can use <=.

Related

SQL Combine Date and Time columns into single DateTime column

I would like to combine two datetime2 columns (called Date and Time respectively) into a single datetime2 column in the same table. The 2 old columns will be removed afterwards.
This query works when testing the combine.
SELECT CAST(DATEADD(day, 0, DATEDIFF(day, 0, Date)) AS DATETIME)
+ CAST(DATEADD(day, 0 - DATEDIFF(day, 0, Time), Time) AS DATETIME)
FROM dbo.Orders
Currently I've tried the following, but I can't figure out how to put the results into an existing column called OrderDate.
SOLVED
UPDATE dbo.Orders
SET
OrderDate = CAST(DATEADD(day, 0, DATEDIFF(day, 0, Date)) AS DATETIME)
+ CAST(DATEADD(day, 0 - DATEDIFF(day, 0, Time), Time) AS DATETIME);
Why the subquery? Just use the expression directly. (Just for the record: Maybe the expression can be optimized/simplyfied as well, I didn't look into this.)
UPDATE dbo.orders
SET orderdate = cast(dateadd(DAY, 0, datediff(DAY, 0, date)) AS datetime)
+ cast(dateadd(DAY, 0 - datediff(DAY, 0, time), time) AS datetime);
Unless all the times in the time column end with milliseconds that have a 0, 3, or 7 in the units position (I would never bank on such a thing), there will be rounding errors when you convert the time to DATETIME.
Here are two of the more extreme examples of what can happen when the units position of time is a "9" during conversion from DATETIME2 to DATETIME.
--========================================================================================
-- Demonstrate the erroneous results of using a conversion to DATETIME.
--========================================================================================
DROP TABLE IF EXISTS #TestTable; --Just to make reruns in SSMS easier.
GO
--===== Create and populate the test table on-the-fly.
SELECT v1.RowNum
,Date = CONVERT(DATETIME2,v1.Date)
,Time = CONVERT(DATETIME2,v1.Time)
,v1.Expected
INTO #TestTable
FROM (VALUES
(1,'2022-12-21','22:59:59.999','2022-12-21 22:59:59.999')
,(2,'2022-12-31','23:59:59.999','2022-12-31 23:59:59.999')
)v1(RowNum,Date,Time,Expected)
;
--===== Show that the currently accepted answer (posted on Mar 2, 2022) is INCORRECT.
WITH cteCombine AS
(
SELECT *
,Combined = cast(dateadd(DAY, 0, datediff(DAY, 0, date)) AS datetime)
+ cast(dateadd(DAY, 0 - datediff(DAY, 0, time), time) AS datetime)
FROM #TestTable
)
SELECT *
,Result = IIF(CONVERT(DATETIME2,Combined) = CONVERT(DATETIME2,Expected)
,'Correct','Incorrect')
FROM cteCombine
ORDER BY RowNum
;
Here are the results from the test code above. The "Combined" column doesn't match the "Expected" column.
Here's one way to overcome the sometimes extreme rounding issues of DATETIME using the same data as the test code above.
--========================================================================================
-- Demonstrate the results of a correct method to avoid the rounding of DATETIME.
--========================================================================================
DROP TABLE IF EXISTS #TestTable; --Just to make reruns in SSMS easier.
GO
--===== Create and populate the test table on-the-fly.
SELECT v1.RowNum
,Date = CONVERT(DATETIME2,v1.Date)
,Time = CONVERT(DATETIME2,v1.Time)
,v1.Expected
INTO #TestTable
FROM (VALUES
(1,'2022-12-21','22:59:59.999','2022-12-21 22:59:59.999')
,(2,'2022-12-31','23:59:59.999','2022-12-31 23:59:59.999')
)v1(RowNum,Date,Time,Expected)
;
--===== Show Correct Method.
WITH cteCombine AS
(
SELECT *
,Combined = CONVERT(DATETIME2
,CONVERT(VARBINARY(6),CONVERT(TIME,Time))
+CONVERT(BINARY(3),CONVERT(DATE,Date)))
FROM #TestTable
)
SELECT *
,Result = IIF(CONVERT(DATETIME2,Combined) = CONVERT(DATETIME2,Expected)
,'Correct','Incorrect')
FROM cteCombine
ORDER BY RowNum
;
And here are the results from that bit of code. The "Combined" column matches the "Expected Column"

How to get date difference in SQL Server and return value

How to get day difference from when the user registered to current date? I have this scenario:
I have some fixed value in master table like [0, 6, 12, 18, 24, 30, 36, 42 .....]
and suppose
day difference is greater or equal than 1 and less than 6 then It should be return 1.
day difference is greater than 6 and less than 12 then it should return 2 and so on.
day difference is greater than 12 and less than 18 then return 3.
day difference is greater than 18 and less than 24 then return 4.
.
.
.
And so on.
I don't want to use case statements because values in master table can not be fix but value pattern will be fix. table value pattern is like that:
common difference between two consecutive values is 6 i.e.
if n=0 then
n+1 = (0 + 6) => 6
Thanks
declare #day int;
declare #regdate datetime = '2019-12-09 19:24:19.623';
declare #currentDate datetime = GETDATE();
SET #day = (SELECT DATEDIFF(day, #regdate, #currentDate) % 6 FROM tblMembers WHERE Id = 1)
PRINT #day
I think that you are looking for integer division, not modulo. This is the default behavior in SQL Server when both arguments are integers, so, since DATEDIFF returns an integer, this should do it:
1 + DATEDIFF(day, #regdate, #currentDate) / 6
Here's approach you can build your solution on:
declare #masterTable table (id int, col int);
insert into #masterTable values
(1,0) ,
(2,6) ,
(3,12),
(4,18),
(5,24),
(6,30),
(7,36),
(8,42),
(9,48);
-- test data
declare #day int;
declare #regdate datetime = '2019-12-09 19:24:19.623';
declare #currentDate datetime = GETDATE();
select #day = datediff(day, #regdate, #currentDate)
;with cte as (
select id,
col lowerBound,
-- here we need to provide some fallback value for last record
coalesce(lead(col) over (order by id), 1000) upperBound
from #masterTable
)
select id from (values (#day)) [day]([cnt])
join cte on [day].[cnt] between cte.lowerBound and cte.upperBound

CTE : looping data input every 15 days| SQL, SQL SERVER

I have this test code from the guides here that I modified for test practice.
What I'm trying to achieve in this code is for it to loop the input based on a certain number, and it will loop with a 15 day gap.
Scenario: I will set the looping into 5 times. And the start date of the loop is 08/15/2019
So, the data that should be inputted is 08/15/2019, 08/30/2019, 09/15/2019, 09/30/2019, 10/15/2019 (Assuming that all dates are in 30 days.)
DECLARE #MYTESTTABLE TABLE ([Month] date, [Data 1] INT, [Data 2] INT, [Data 3] INT);
WITH MYLOOP AS (
SELECT CAST('2019-01-01' AS DATE) AS [date], 100 as [Data 3]
UNION ALL
SELECT DATEADD(MONTH, 1, [date]), [Data 3]=[Data 3]+1
FROM MYLOOP
WHERE DATEADD(MONTH, 1, [date]) < CAST('2022-01-01' AS DATE) )
INSERT INTO #MYTESTTABLE ([Month], [Data 1], [Data 2], [Data 3])
SELECT LEFT([date],10) as [Month], 100 AS [Data 1], 100 AS [Data 2], [Data 3]
FROM MYLOOP
SELECT * FROM #MYTESTTABLE
This is the guide that I got based on that scenario.
My problem now is that I'm having a hard time implementing the 2 SQL Queries into 1.
DECLARE #YourTable table (YourDate int, valuex int,tests int)
insert into #YourTable VALUES ('100',5,11)
;WITH AllNumbers AS
(SELECT 1 AS Number, 100 AS [value]
UNION ALL
SELECT Number+1, [value]=[value]+1
FROM AllNumbers
WHERE Number<=15-1 )
SELECT
YourDate, valuex, tests
FROM #YourTable y
INNER JOIN AllNumbers a ON 1=1
This is what I got so far.
DECLARE #MYTESTTABLE TABLE ([Month] date, [Data 1] INT, [Data 2] INT, [Data 3] INT);
WITH MYLOOP AS (
SELECT CAST('2019-01-01' AS DATE) AS [date], 1 as [Data 2], 100 as [Data 3]
UNION ALL
SELECT DATEADD(MONTH, 1, [date]), [Data 2]+1, [Data 3]=[Data 3]+1
FROM MYLOOP
WHERE [Data 2] < 7 )
INSERT INTO #MYTESTTABLE ([Month], [Data 1], [Data 2], [Data 3])
SELECT LEFT([date],10) as [Month], 100 AS [Data 1], [Data 2], [Data 3]
FROM MYLOOP
SELECT * FROM #MYTESTTABLE
How do is set to to trigger, or add every 15 days, and not every 1 month(30 days)?
May I ask for your assistance on this matter?
If you are looking for 5 dates only from your input date, this following script will work-
Input date must need to be 15 or 30 of a month
DECLARE #st_dt DATE = '08/15/2019'
DECLARE #prev_dt DATE
DECLARE #LC INT =0
DECLARE #MYTESTTABLE TABLE ([Month] date)
WHILE #LC < 5
BEGIN
IF #LC = 0
BEGIN
INSERT INTO #MYTESTTABLE ([Month]) VALUES(#st_dt)
SET #prev_dt = #st_dt
END
ELSE
BEGIN
IF DATEPART(DD,#prev_dt) = 15
BEGIN
INSERT INTO #MYTESTTABLE ([Month]) VALUES(DATEADD(DD,(CASE WHEN DATEPART(MM,#prev_dt) = 2 THEN 13 ELSE 15 END) ,#prev_dt))
SET #prev_dt = DATEADD(DD,(CASE WHEN DATEPART(MM,#prev_dt) = 2 THEN 13 ELSE 15 END) ,#prev_dt)
END
ELSE
BEGIN
INSERT INTO #MYTESTTABLE ([Month])
VALUES(DATEADD(DD,(15-DATEPART(DD,DATEADD(MM,1,#prev_dt))),DATEADD(MM,1,#prev_dt)))
SET #prev_dt = DATEADD(DD,(15-DATEPART(DD,DATEADD(MM,1,#prev_dt))),DATEADD(MM,1,#prev_dt))
END
END
SET #LC = #LC + 1
END
SELECT * FROM #MYTESTTABLE
Output is-
Month
2019-08-15
2019-08-30
2019-09-15
2019-09-30
2019-10-15
Now, If only adding +15 days with previous date can satisfy your requirement, this is easy and this following script will work for you-
DECLARE #st_dt DATE = '8/01/2019'
DECLARE #prev_dt DATE
DECLARE #LC INT =0
DECLARE #MYTESTTABLE TABLE ([Month] date)
WHILE #LC < 5
BEGIN
IF #LC = 0
BEGIN
INSERT INTO #MYTESTTABLE ([Month]) VALUES(#st_dt)
SET #prev_dt = #st_dt
END
ELSE
BEGIN
INSERT INTO #MYTESTTABLE ([Month]) VALUES(DATEADD(DD,15,#prev_dt))
SET #prev_dt = DATEADD(DD,15,#prev_dt)
END
SET #LC = #LC + 1
END
SELECT * FROM #MYTESTTABLE
This is the CTE version
-- straight 15 days addition
declare #startdate date = '2019-08-15'
declare #loop smallint = 15
;with cte_count as (
select 0 ctr
union all
select ctr + 1 from cte_count
where ctr < #loop-1
)
select dateadd(day,ctr*15,x.newdate) [Date] from cte_count
cross join
(select #startdate newdate) x
--- below shows 15th day and end of month
declare #startdate date = '2019-08-15'
declare #loop smallint = 50
;with cte_count as (
select 0 ctr
union all
select ctr + 1 from cte_count
where ctr < #loop-1
)
select
distinct
cast(iif(day(dateadd(day,ctr*15,newdate))<=15,
dateadd(day,14,DATEADD(m, DATEDIFF(m, 0, eomonth(dateadd(day,ctr*15,newdate))), 0))
,eomonth(dateadd(day,ctr*15,newdate))) as date)
from cte_count
cross join
(select #startdate newdate) x

How can i improve performance of a scalar value function used in join clause?

I have developed a query to process the data for a report. I had to get only one record from the history table for the selected month.
For example: These are 4 vehicles. Vehicle "1" is modified four times in November 2018 on the following dates and prices:
1). 05 November 2018 with price 1000
2). 15 November 2018 with price 5000
3). 25 November 2018 with price 8000
Now i wanted the only one record on last modified date (25 November).
I developed a database scalar function and used that function in join clause which worked perfectly with less data. Now we have some records (not too much just 100 records in history table) and the report is suffering from speed/performance issues. I created indexes and removed sub-queries but still couldn't be much helpful.
Please see my tried scripts:
declare #pIntFranchiseId int = 16179;
declare #pDtFrom date = '2018-12-01';
declare #pDtTo date = '2018-12-28';
SELECT ba.[Id],BKAgrHistory.[BookingStart],BKAgrHistory.[BookingEnd],BKAgrHistory.[StartTime],BKAgrHistory.[EndTime]
,ba.[FranchiseId],BKAgrHistory.[FleetId],[BKRenterId],[IsReportGenerated],[StepsCompleted]
,ba.[BookingId],ba.[ReservationId],ba.[RentalAgreementId],[VehicleUsedFor],[OperatorLicence]
,[ClearanceCode],[ExcessAmount],[ClearanceCodeAdditionalDriv],[ExcessAmountAdditionalDriv],[IsClearanceVerified]
,[BKDiscount],BKAgrHistory.[AgreementStatus],[IsTravelingOutsideUk],[OtherCountries],[RentalType],[RenterType],[RenterCompanyType]
,[IsVehicleDelivered],[IsReturnedOnSameAddress],[PickUpAddress],[ReturnAddress],[OtherReturnAddress],[TariffId]
,[SpecialRateId],[IsChargeShortWeekend],[IsChargeLongWeekend],[ChargeHalfDay],[IsDamageProvided],[IsRenterAgreement]
,[BKIsOwnInsurance],[ExpectedMileage],BKAgrHistory.[TotalHireCharge],[BKFreeMiles],[BKExpensePerMile],[BKCollisionDamage],[BKDamage]
,[BKTheft],[BKOverHeight],[BKBookingNotes],[BKFleetCurrentMileage],[BKMileageOut],[BKMileageIn],[BKDamageOut],[BKDamageIn],[Condition]
,[AppliedRates],[Paid],[OnceInvoiced],[InterimInvoiceCount],[PBNumber],[Excess],[StatusId],[CreatedOn],[ModifiedOn],BKAgrHistory.[BookingDays]
,[VATPercentage],[BkOpeningHrsSurcharge],[DrivenMileage],[ParentRAId],[NetTotal],[VatTotal],[GrossTotal],[GrandInsurableRevenue]
,[GrandTotalWithoutExtra],[GrandInsurableRevWithoutExtra],[TariffNetPrice],[OnceMailed],[LastBookingEnd],[LastEndTime],[BkOpeningHrsSurchargeNet]
,[BkOpeningHrsSurchargeVat],BKAgrHistory.[DailyHireCharge],[BkNetExtraDrivenMilesExcess],[BkExtraDrivenMilesExcessVAT],[BkTotalExtraDrivenMilesExcess],[ExcessMilesDays]
,[BkWebWeekendSurcharge],BKAgrHistory.[InsertedUserId],BKAgrHistory.[UpdateUserId],BKAgrHistory.[InsertedDate],BKAgrHistory.[UpdatedDate],BKAgrHistory.[InsertedUserName],BKAgrHistory.[UpdatedUserName],
[SecurityDeposit],[SecurityDepositRuleId]
FROM [dbo].[BookingAgreements] ba
join BookingAgreementAmountHistory BKAgrHistory on BKAgrHistory.Id = dbo.[GetAgreementAmtHistoryId](#pIntFranchiseId,ba.Id,#pDtFrom,#pDtTo)
where
(
DATEADD(day, DATEDIFF(day, 0,BKAgrHistory.BookingStart), 0) <= DATEADD(day, DATEDIFF(day, 0, #pDtFrom), 0) OR
DATEADD(day, DATEDIFF(day, 0,BKAgrHistory.BookingEnd), 0) <= DATEADD(day, DATEDIFF(day, 0, #pDtFrom), 0)
)
and ba.FranchiseId=#pIntFranchiseId and StatusId=1
and ISNULL(BKAgrHistory.AgreementStatus,0) not in (0,2,6,7)
and BKAgrHistory.FleetId in (
select f.Id from Fleets f
join FleetsHistory fleetHis on fleetHis.Id = dbo.[GetVehicleHistoryById](#pIntFranchiseId,BKAgrHistory.FleetId,#pDtFrom,#pDtTo)
where fleetHis.GhostVehicle = 0 and fleetHis.CoreFleet = 1 and isnull(fleetHis.StatusId,0) in (1,4) and
f.Franchise_Id=#pIntFranchiseId and DATEADD(day, DATEDIFF(day, 0, fleetHis.PurchaseDate), 0) <= DATEADD(day, DATEDIFF(day, 0, #pDtTo), 0));
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[GetVehicleHistoryById](#franchiseId int = NULL,#fleetId int = NULL, #fromDate Date = NULL, #toDate Date = NULL)
RETURNS int
AS BEGIN
Declare #returnId int = 0;
Select top 1 #returnId = isnull(Id,0) from FleetsHistory
where TransactionDate =
(Select max(TransactionDate)
from FleetsHistory fh
where fh.Franchise_Id = #franchiseId and fh.Fleet_Id = #fleetId
--group by AgreementId
and (DATEADD(day, DATEDIFF(day, 0, TransactionDate),0)) <= DATEADD(day, DATEDIFF(day, 0, #toDate),0))
order by Id desc;
Return #returnId;
END
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[GetAgreementAmtHistoryId](#franchiseId int = NULL,#agreementId int = NULL, #fromDate Date = NULL, #toDate Date = NULL)
RETURNS int
AS BEGIN
Declare #returnId int = 0;
Select top 1 #returnId = isnull(Id,0) from BookingAgreementAmountHistory
where TransactionDate =
(Select max(TransactionDate)
from BookingAgreementAmountHistory
where FranchiseId = #franchiseId and AgreementId = #agreementId
--group by AgreementId
and (DATEADD(day, DATEDIFF(day, 0, TransactionDate),0)) <= DATEADD(day, DATEDIFF(day, 0, #toDate),0))
order by Id desc;
Return #returnId;
END
Whatever else functions might do, they do not improve the performance of SQL queries.
I would suggest using apply with the right arguments. It is a little hard to see what you really want, given the proliferation of queries in the question, the lack of sample data and desired results, and the disconnect between your simple question and the actual query. But the idea would be:
select ba.*, baah.*
from [dbo].[BookingAgreements] ba outer apply
(select top (1) baah.*
from BookingAgreementAmountHistory baah
where baah.Id = ba.FranchiseId and
baah.AgreementStatus not in (2, 6, 7) and -- this takes care of NULL values
baah.<date> >= #pDtFrom and
baah.<date> <= #pDtTo
order by baah.<date> desc
) baah
where ba.FranchiseId = #pIntFranchiseId;
<date> is for the modification date that you mention at the beginning of the question. It is quite unclear which date this actually refers to.

Dynamic SQL column names

I have a query like:
SELECT [Week 1].Product, [Week 2].Product, [Week 3].Product, [Week 4].Product, Sum([Week 1].Transaction_Amount), Sum([Week 2].Transaction_Amount), Sum([Week 3].Transaction_Amount), Sum([Week 4].Transaction_Amount)
FROM [Week 2],[Week 3],[Week 3],[Week 4];
I have data for 70 weeks name [week 1] to [week 70]
Is it possible to make [week 1],[Week 2],[Week 3],[Week 4] dynamic.
(i,e) have a master table where I can have 4 week names like [Week 8], [Week 6], [Week 45], [Week 18] and replace the [Week 1], [Week 2], [Week 3], [Week 4] with the above 4 in my query
IT IS AN MS ACCESS APPLICATION. Sorry I did not mention previously.
Yes you can do that using a dynamic query and then using EXEC or sp_executesql to execute the query
The following will be a pseudo code which you can improvise as per your requirement.
DECLARE #STartCount, #TableCount, #Query, #SubQuery, #WeekTblName
SET #StartCount = 1
SELECT #TableCount = Count(*) FROM Master_Table
WHILE(#StartCount <= #TableCount)
BEGIN
SELECT #WeekTblName = Col_Name FROM Master_Table WHERE ID = #StartCount
SET #SubQuery = #SubQuery + #WeekTblName + ' AS [Week ' + #STartCount + '], '
SET #StartCount = #StartCount + 1
END
SET #SUBQUERY = LEFT(#SUBQUERY, LEN(#SUBQUERY) - 2);
SET #Query = 'SELECT [Week 1].Product, [Week 2].Product, [Week 3].Product, [Week 4].Product, Sum([Week 1].Transaction_Amount), Sum([Week 2].Transaction_Amount), Sum([Week 3].Transaction_Amount), Sum([Week 4].Transaction_Amount)
FROM ' + #SubQuery
EXEC(#Query)
Hope this helps
UPDATE
Create some VBA code on a button which dynamically creates the select query. How to build a dynamic query
You could:
1) Change your table structure so that you have a single Table for all weeks e.g. Week (ID, WeekNumber, column1, column2...). And then in your Select statement you can add a where clause to use a parameter to select which weeks you are interested in. This is dependant on all the weeks tables having the same structure.
2) Use Dynamic SQL to build your select statement and replace your table names. See sp_executesql