This question already has answers here:
SQL Server Query: Fast with Literal but Slow with Variable
(8 answers)
Closed 10 months ago.
I found a big difference of the query execution under MS SQL Server Standart 2019.
T-SQL
DECLARE #atTime datetime2 = '2022-05-04 13:23:20';
DECLARE #startTime datetime2;
DECLARE #shiftTime datetime2;
SET #startTime = #atTime;
SET #shiftTime = DATEADD(SECOND, -5, #atTime)
-- SELECT #shiftTime, #startTime
-- 2022-05-04 13:23:15.0000000 2022-05-04 13:23:20.0000000
-- #1 It takes 7 seconds to complete
SELECT TOP(1) * FROM [TrackerPositions] WITH(NOLOCK) WHERE AtTime BETWEEN #shiftTime AND #startTime
-- #1 It takes 0 seconds to complete
SELECT TOP(1) * FROM [TrackerPositions] WITH(NOLOCK) WHERE AtTime BETWEEN '2022-05-04 13:23:15.0000000' AND '2022-05-04 13:23:20.0000000'
Note: AtTime colum has datetime2
Please, help to get working fast SELECT #1
Thank you!
UPDATE #1
CREATE TABLE [dbo].[TrackerPositions](
[ID] [uniqueidentifier] NOT NULL,
[GPSTrackerID] [int] NOT NULL,
[AtTime] [datetime2](7) NOT NULL,
[Lat] [decimal](9, 6) NOT NULL,
[Lng] [decimal](9, 6) NOT NULL,
[GeoLocation] AS ([geography]::STGeomFromText(((('POINT('+CONVERT([varchar](20),[Lng],0))+' ')+CONVERT([varchar](20),[Lat],0))+')',(4326))),
[SignalLevel] [int] NULL,
[IPAddress] [nvarchar](40) NULL,
[Port] [int] NULL,
[Height] [int] NULL,
[IsMoving] [bit] NULL,
[Speed] [decimal](18, 4) NULL,
CONSTRAINT [PK_TrackerPositions] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 50, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[TrackerPositions] ADD CONSTRAINT [DF_TrackerPositions_ID] DEFAULT (newid()) FOR [ID]
GO
ALTER TABLE [dbo].[TrackerPositions] ADD CONSTRAINT [DF_TrackerPositions_IsMoving] DEFAULT ((0)) FOR [IsMoving]
GO
ALTER TABLE [dbo].[TrackerPositions] WITH CHECK ADD CONSTRAINT [FK_TrackerPositions_GPSTrackers] FOREIGN KEY([GPSTrackerID])
REFERENCES [dbo].[GPSTrackers] ([ID])
GO
ALTER TABLE [dbo].[TrackerPositions] CHECK CONSTRAINT [FK_TrackerPositions_GPSTrackers]
GO
The right answer is to use OPTION(RECOMPILE)
SELECT TOP(1) * FROM [TrackerPositions] WITH(NOLOCK) WHERE AtTime BETWEEN #shiftTime AND #startTime OPTION(RECOMPILE)
Related
I am trying to export some records from ServerA.DatabaseA..Anchor to ServerB.DatabaseB..Anchor. The source is active. The destination is a development database. The destination has one additional field, a tenancy id.
SQL for Source table
CREATE TABLE [dbo].[Anchor](
[AnchorId] [int] IDENTITY(130,1) NOT NULL,
[NodeId] [int] NOT NULL,
[UnitName] [varchar](200) NOT NULL,
[SetPosition] [varchar](5) NOT NULL,
[CreateDate] [datetime] NOT NULL,
[StartDate] [datetime] NOT NULL,
[EndDate] [datetime] NOT NULL,
[Latitude] [float] NULL,
[Longitude] [float] NULL,
[RadiusMeters] [int] NOT NULL,
[Creator] [varchar](100) NULL,
[Cellnumber1] [varchar](20) NULL,
[Cellnumber2] [varchar](20) NULL,
[Cellnumber3] [varchar](20) NULL,
[EmailTo] [varchar](255) NULL,
[UseMsgFwdContacts] [bit] NOT NULL,
CONSTRAINT [PK_Anchor] PRIMARY KEY CLUSTERED
(
[AnchorId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Anchor] ADD CONSTRAINT [DF_Anchor_UseMsgFwdContacts] DEFAULT ((0)) FOR [UseMsgFwdContacts]
GO
SQL Query for selecting records
Datetime's are cast to datetime for testing
SELECT [AnchorId]
,'MXM' as OrgCode -- the new tenancy field
,[NodeId]
,[UnitName]
,[SetPosition]
,CAST([CreateDate] as DATETIME) as [CreateDate]
,CAST([StartDate] as DATETIME) as [StartDate]
,CAST([EndDate] as DATETIME) as [EndDate]
,[Latitude]
,[Longitude]
,[RadiusMeters]
,[Creator]
,[Cellnumber1]
,[Cellnumber2]
,[Cellnumber3]
,[EmailTo]
,[UseMsgFwdContacts]
FROM [client_Maxam].[dbo].[Anchor]
WHERE AnchorId >335
Click Ok and Next, and now...
Why is my source datatype being picked up as datetime2, preventing me from running the export.
I'm trying to an insert from one database table to another like this:
INSERT INTO factoryDB_Development.dbo.engineList
SELECT *
FROM factoryDB_Staging.dbo.engineList
WHERE engineID NOT IN (SELECT engineID
FROM factoryDB_Development.dbo.engineList)
But I am getting this error:
Cannot insert duplicate key in object 'dbo.engineList'
I am assume it's because that ID already exists in factoryDB_Development.dbo.engineList.
When I checked online at the Microsoft site, I just saw this:
To work around this issue, enable trace flag 8690 to disable the Spool operation
Is there a way to check to see if a row or ID already exists before doing the insert?
Thanks!
If I understood your problem correctly, you want to avoid inserting record in target table if that record with the same ID already exists.
If the ID column in question is a primary key, you can simply set IGNORE_DUP_KEY = ON by recreating the index.
Below is the sample example.
CREATE TABLE [dbo].[Source](
[ID] [int] NULL,
[Name] [varchar](5) NULL
) ON [PRIMARY]
CREATE TABLE [dbo].[Target](
[ID] [int] NOT NULL,
[Name] [varchar](5) NULL,
PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = ON, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
--input sample values in source
INSERT INTO [Source] VALUES (1, 'A') , (1, 'A') , (2, 'B')
--insert data from source to target
INSERT INTO Target
SELECT * FROM Source
DROP TABLE SOURCE
DROP TABLE TARGET
I am trying to re-calculate a few different columns of data for a particular EmployeeID.
I want the hrs_YTD column to keep a running total. What is a good way of updating this info?
HRS_YTD currently has 0.00 values. I wan't to achieve the results in the table below.
ID | CHEKDATE | CHEKNUMBR | HRS | HRS_YTD
EN344944 | 01/1/2014 | dd1001 | 40.00 | 40.00
EN344944 | 01/8/2014 | dd1002 | 30.00 | 70.00
EN344944 | 1/15/2014 | dd1003 | 32.50 | 102.50
etc.....
DECLARE #k_external_id varchar(32)
SET #k_external_id = 'EN344944'
SELECT * INTO #tmpA
FROM dbo.gp_check_hdr a
WHERE a.EMPLOYID = #k_external_id
SELECT a.ID, a.CHEKNMBR, a.CHEKDATE,
(SELECT CAST(SUM(a.[hours]) as decimal(18,2)) FROM #tmpA b
WHERE (b.CHEKDATE <= a.CHEKDATE and YEAR(b.CHEKDATE) = 2013)) AS hrs_ytd
FROM #tmpA a
WHERE YEAR(a.CHEKDATE) = 2013
I really don't know if I can alias a table like I did with #tmpA b, but it's worked for me in the past. That doesn't mean its a good way of doing things though. Can someone show me a way to achieve the results I need?
havent tested this, but you can give this a try
DECLARE #k_external_id varchar(32)
SET #k_external_id = 'EN344944'
SELECT g1.primarykey, g1.ID,g1.CHEKDATE, g1.CHEKNUMBR, g1.HRS ,(SELECT SUM(g2.HRS)
FROM dbo.gp_check_hdr g2
WHERE g2.ID = #k_external_id AND
(g2.primarykey <= g1.primarykey)) as HRS_YTD
FROM dbo.gp_check_hdr g1
WHERE g1.ID = #k_external_id
ORDER BY g1.primarykey;
http://www.codeproject.com/Articles/300785/Calculating-simple-running-totals-in-SQL-Server
The way I'd do this is a combination of a computed column and a user defined function.
The function allows to aggregate the data. In a computed column, you can only work with fields of the same row, hence calling a function (which is allowed) is necessary.
The computed column allows this to work continuously without any additional queries or temp tables, etc. Once it's set, you don't need to run nightly updates or triggers or anything of the sort to keep the data updated, including when records change or get deleted.
Here's my solution ... and SqlFiddle: http://www.sqlfiddle.com/#!3/cd8d6/1/0
Edit:
I've updated this to reflect your need to calculate the running totals per employee. SqlFiddle also updated.
The function:
Create Function udf_GetRunningTotals (
#CheckDate DateTime,
#EmployeeID int
)
Returns Decimal(18,2)
As
Begin
Declare #Result Decimal(18,2)
Select #Result = Cast(Sum(rt.Hrs) As Decimal(18,2))
From RunningTotals rt
Where rt.CheckDate <= #CheckDate
And Year(rt.CheckDate) = Year(#CheckDate)
And rt.EmployeeID = #EmployeeID
Return #Result
End
The Table Schema:
Create Table [dbo].[RunningTotals](
[ID] [int] Identity(1,1) NOT NULL,
[EmployeeID] [int] NOT NULL,
[CheckDate] [datetime] NOT NULL,
[CheckNumber] [int] NOT NULL,
[Hrs] [decimal](18, 2) NOT NULL,
[Hrs_Ytd] AS ([dbo].[udf_GetRunningTotals]([CheckDate],[EmployeeID])), -- must add after table creation and function creation due to inter-referencing of table and function
Constraint [PK_RunningTotals3] Primary Key Clustered (
[ID] ASC
) With (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
)
) On [PRIMARY]
Result will tally up the YTD hrs for each year.
Note --
You cannot create the function or the table as is since they reference each other.
First, create the table with all but the computed column;
Then, create the function.
Finally, alter the table and add the computed column.
Here's a full running test script:
-- Table schema
Create Table [dbo].[RunningTotals](
[ID] [int] Identity(1,1) NOT NULL,
[EmployeeID] [int] NOT NULL,
[CheckDate] [datetime] NOT NULL,
[CheckNumber] [int] NOT NULL,
[Hrs] [decimal](18, 2) NOT NULL,
Constraint [PK_RunningTotals3] Primary Key Clustered (
[ID] ASC
) With (
PAD_INDEX = OFF,
STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON
)
) On [PRIMARY]
Go
-- UDF Function to compute totals
Create Function udf_GetRunningTotals (
#CheckDate DateTime,
#EmployeeID int
)
Returns Decimal(18,2)
As
Begin
Declare #Result Decimal(18,2)
Select #Result = Cast(Sum(rt.Hrs) As Decimal(18,2))
From RunningTotals rt
Where rt.CheckDate <= #CheckDate
And Year(rt.CheckDate) = Year(#CheckDate)
And rt.EmployeeID = #EmployeeID
Return #Result
End
Go
-- Add the computed column to the table
Alter Table RunningTotals Add [Hrs_Ytd] As (dbo.udf_GetRunningTotals(CheckDate, EmployeeID))
Go
-- Insert some test data
Insert into RunningTotals Values (334944, '1/1/2014', '1001', 40.00)
Insert into RunningTotals Values (334944, '1/5/2014', '1002', 30.00)
Insert into RunningTotals Values (334944, '1/15/2014', '1003', 32.50)
Insert into RunningTotals Values (334945, '1/5/2014', '1001', 10.00)
Insert into RunningTotals Values (334945, '1/6/2014', '1002', 20.00)
Insert into RunningTotals Values (334945, '1/8/2014', '1003', 12.50)
-- Test the computed column
Select * From RunningTotals
Your sub query should work just fine.
I used a table variable in place of a temp table.
I also limited the results inserted in the temp table to 2013 to simplify the final select statement and limit the results in the temp table to just what you need. The only other thing is joining the sub query to the main query using the ID but what you have should work as you are limiting the result in your temp table to a specific ID.
DECLARE
#k_external_id varchar(32)
,#k_reporting_year int
SET #k_external_id = 'EN344944'
SET #k_reporting_year = 2013
DECLARE #temp TABLE(
ID NVARCHAR(32)
,CheckDate DATE
,CheckNumber NVARCHAR(6)
,HRS DECIMAL(18,2)
)
INSERT INTO #temp (
ID
,CheckDate
,CheckNumber
,HRS
)
SELECT
ID
,CHEKDATE
,CHEKNMBR
,[hours]
FROM
dbo.gp_check_hdr
WHERE
EMPLOYID = #k_external_id
AND YEAR(a.CHEKDATE) = #k_reporting_year
SELECT
ID
,CheckDate
,CheckNumber
,HRS
,(SELECT SUM(HRS) FROM #temp b WHERE a.ID = b.ID AND b.CheckDate <= a.CheckDate) AS hrs_ytd
FROM
#temp a
T-SQL Insert into multiple linked tables using a condition and without using a cursor.
Hello,
I have the following tables
CREATE TABLE [dbo].[TestMergeQuote](
[uid] [uniqueidentifier] NOT NULL,
[otherData] [nvarchar](50) NULL,
CONSTRAINT [PK_TestMergeQuote] PRIMARY KEY CLUSTERED
(
[uid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [dbo].[TestMergeQuote] ADD CONSTRAINT [DF_TestMergeQuote_uid] DEFAULT (newid()) FOR [uid]
--=============
CREATE TABLE [dbo].[TestMergeClient](
[id] [int] IDENTITY(1,1) NOT NULL,
[otherData] [nvarchar](50) NULL,
CONSTRAINT [PK_TestMergeClient] PRIMARY KEY CLUSTERED
(
[id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
--==============
CREATE TABLE [dbo].[TestMergeDocument](
[id] [int] NOT NULL,
[uid_quote] [uniqueidentifier] NOT NULL,
[id_owner] [int] NOT NULL,
[id_keeper] [int] NULL,
[otherData] [nvarchar](50) NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[TestMergeDocument] WITH CHECK ADD CONSTRAINT [FK_TestMergeDocument_TestMergeClient_Keeper] FOREIGN KEY([id_keeper])
REFERENCES [dbo].[TestMergeClient] ([id])
GO
ALTER TABLE [dbo].[TestMergeDocument] CHECK CONSTRAINT [FK_TestMergeDocument_TestMergeClient_Keeper]
GO
ALTER TABLE [dbo].[TestMergeDocument] WITH CHECK ADD CONSTRAINT [FK_TestMergeDocument_TestMergeClient_Owner] FOREIGN KEY([id_owner])
REFERENCES [dbo].[TestMergeClient] ([id])
GO
ALTER TABLE [dbo].[TestMergeDocument] CHECK CONSTRAINT [FK_TestMergeDocument_TestMergeClient_Owner]
GO
ALTER TABLE [dbo].[TestMergeDocument] WITH CHECK ADD CONSTRAINT [FK_TestMergeDocument_TestMergeQuote] FOREIGN KEY([uid_quote])
REFERENCES [dbo].[TestMergeQuote] ([uid])
GO
ALTER TABLE [dbo].[TestMergeDocument] CHECK CONSTRAINT [FK_TestMergeDocument_TestMergeQuote]
GO
AND also table X with other various data.
I want to insert into these three tables the data that already exists in these 3 tables, but giving it different id's, and also replacing some of the data within the X table.
It's a sort of a "copy the data from last year", but add new info.
The condition is that id_keeper is sometimes null, and no insert should be done for it.
I am aware that I have to use OUTPUT and MERGE, but I have no ideea how to achieve something this complex.
The CRUDE code for this using a cursor would be:
DECLARE #OldIdDocument INT, #NewIdDocument INT
DECLARE #OldIdOwner INT, #NewIdOwner INT
DECLARE #OldIdKeeper INT, #NewIdKeeper INT
DECLARE #OldIdQuote UNIQUEINDETIFIER, #NewIdQuote UNIQUEINDETIFIER,
INSERT INTO TestMergeQuote(otherData)
SELECT TOP(1) otherData FROM TestMergeQuote WHERE uid = #OldIdQuote
SET #NewIdQuote = ##IDENTITY
INSERT INTO TestMergeClient(otherData)
SELECT TOP(1) otherData FROM TestMergeClient WHERE uid = #OldIdOwner
SET #NewIdOwner = ##IDENTITY
IF(#OldIdKeeper IS NOT NULL)
BEGIN
INSERT INTO TestMergeClient(otherData)
SELECT TOP(1) otherData FROM TestMergeClient WHERE uid = #OldIdKeeper
SET #NewIdKeeper = ##IDENTITY
END
INSERT INTO TestMergeDocument([uid_quote], [id_owner] , [id_keeper], otherData)
SELECT TOP(1) #NewIdQuote , #NewIdOwner , #NewIdKeeper ,otherData FROM TestMergeDocument WHERE uid = #OldIdDocument
SET #NewIdDocument = ##IDENTITY
You shouldn't have to use a cursor. What I would try is to first pump the data out into separate tables so you can manipulate the data to your heart's content.
Something like this first:
select * into TestMergeQuote_Temp from TestMergeQuote
That will make a new table with the data you want to copy. Of course you can add a where clause to filter the data so you aren't copying a very large table.
Then you can add values, change values, delete values on the _Temp versions.
When you are ready you can insert the data back. Of course you might have to turn auto id off if you have a primary key that is auto-incrementing. Or if you just want new id's and don't want to make id's manually, you should be able to insert the new records just fine and have new id's created for you.
But as a start, try pumping the data into new tables and then worry about inserting after that.
I have the following table:
CREATE TABLE [dbo].[Tree](
[AutoID] [int] IDENTITY(1,1) NOT NULL,
[Category] [varchar](10) NULL,
[Condition] [varchar](10) NULL,
[Description] [varchar](50) NULL,
CONSTRAINT [PK_Tree] PRIMARY KEY CLUSTERED
(
[AutoID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
The data looks like this:
INSERT INTO [Test].[dbo].[Tree]
([Category]
,[Condition]
,[Description])
VALUES ('1','Alpha','Type 1')
INSERT INTO [Test].[dbo].[Tree]
([Category]
,[Condition]
,[Description])
VALUES ('1','Alpha','Type 1')
INSERT INTO [Test].[dbo].[Tree]
([Category]
,[Condition]
,[Description])
VALUES ('2','Alpha','Type 2')
INSERT INTO [Test].[dbo].[Tree]
([Category]
,[Condition]
,[Description])
VALUES ('2','Alpha','Type 2')
GO
I try now to do the following:
SELECT Category,COUNT(*) as CategoryCount FROM Tree where Condition = 'Alpha' group by Category
However, I also wish to get the Description for each Element. I tried several subqueries, self joins etc., but I always come to the problem that the subquery cannot return more than one record.
The problem is caused by a poor database design which I cannot change and I have run out of ideas as to how to get this done in a single query.
If you need description, you have to include it in the aggregate
SELECT Category ,
[Description] ,
COUNT(*) AS CategoryCount
FROM Tree
WHERE Condition = 'Alpha'
GROUP BY Category ,
[Description]