Insert to #table much faster than insert to #table. Why? - sql-server

This is the basic structure of my query. If I insert into a #temp table then the query runs in about two seconds:
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
declare #table table
(
locationKey int,
DateKey date,
totalcount bigint,
locationCount int,
LastDateOnAir date,
Aging int
)
;with cteSum as
(
select
fact.locationid as 'locationKey'
,cast([datetime] as date) as 'datekey'
,sum(totalcount) as 'totalcount'
,count(fact.locationCell) as 'locationCount'
,sum(period) as 'period'
FROM [dbo].[MasterTable] fact inner join Dim dim on
fact.locationid = dim.location
WHERE cast([datetime] as date) >= '2017-09-21'
group by
fact.locationid, cast([datetime] as date)
)
select
locationKey, datekey, totalcount, locationCount into #temp
FROM cteSum
--insert into #table
--(locationKey, datekey, totalcount, locationCount)
--select
--locationKey, datekey, totalcount, locationCount
--FROM cteSum
If I insert into the #table variable, the whole query runs in about eight seconds. The insert to a table variable adds six seconds to the execution.
Are table variables that much slower than temp tables? We're talking about 2,000 rows that are being inserted.
Thanks.

For testing performance:
OPTION(RECOMPILE)
OPTION(MAXDOP 1)
SET STATISTICS TIME ON
Temp table & variable table objects are almost identical...

Related

Can I "left join" days between 2 dates in sql server?

There is a table in SQL Server where data is entered day by day. In this table, data is not filled in some days.
Therefore, there are no records in the table.
Sample: dataTable
I need to generate a report like the one below from this table.
Create a table with all the days of the year. I know that I can output a report by "joining" the "dataTable" table.
But this solution seems a bit strange to me.
Is there another way?
the code i use for temp date table
CREATE TABLE tempDate (
calendarDate date,
PRIMARY KEY (calendarDate)
)
DECLARE
#start DATE= '2021-01-01',
#dateCount INT= 730,
#rowNumber INT=1
WHILE (#rowNumber < #dateCount)
BEGIN
INSERT INTO tempDate values (DATEADD(DAY, #rowNumber, #start))
set #rowNumber=#rowNumber+1
END
GO
select * from tempDate
This is how I join using this table
SELECT
*
FROM
tempDate td WITH (NOLOCK)
LEFT JOIN dataTable dt WITH (NOLOCK) ON dt.reportDate = td.calendarDate
WHERE
td.calendarDate BETWEEN '2021-09-05' AND '2021-09-15'
Create a table with all the days of the year. I know that I can output a report by "joining" the "dataTable" table.
This is the way. You can generate that "table" on the fly if you really want to, but normally the best way is to simply have a calendar table.
You can use common expression tables for dates. The code you need:
IF(OBJECT_ID('tempdb..#t') IS NOT NULL)
BEGIN
DROP TABLE #t
END
CREATE TABLE #t
(
id int,
dt date,
dsc varchar(100),
)
INSERT INTO #t
VALUES
(1, '2021.09.08', 'a'),
(1, '2021.09.09', 'b'),
(1, '2021.09.12', 'c')
DECLARE #minDate AS DATE
SET #minDate = (SELECT MIN(dt) FROM #t)
DECLARE #maxDate AS DATE
SET #maxDate = (SELECT MAX(dt) FROM #t)
;WITH cte
AS
(
SELECT #minDate AS [dt]
UNION ALL
SELECT DATEADD(DAY, 1, [dt])
FROM cte
WHERE DATEADD(DAY, 1, [dt])<=#maxDate
)
SELECT
ISNULL(CAST(t.id AS VARCHAR(10)), '') AS [id],
cte.dt AS [dt],
ISNULL(t.dsc, 'No record has been entered in the table.') AS [dsc]
FROM
cte
LEFT JOIN #t t on t.dt=cte.dt
The fastest method is to use a numbers table, you can get a date list between 2 dates with that:
DECLARE #Date1 DATE, #Date2 DATE
SET #Date1 = '20200528'
SET #Date2 = '20200625'
SELECT DATEADD(DAY,number+1,#Date1) [Date]
FROM master..spt_values
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2
If you go go in LEFT JOIN this select, whit your table, you have the result that you want.
SELECT *
FROM (SELECT DATEADD(DAY,number+1,#Date1) [Date]
FROM master..spt_values WITH (NOLOCK)
WHERE type = 'P'
AND DATEADD(DAY,number+1,#Date1) < #Date2 ) as a
LEFT JOIN yourTable dt WITH (NOLOCK) ON a.date = dt.reportDate
WHERE td.[Date] BETWEEN '2021-09-05' AND '2021-09-15'

How do I loop through a table, search with that data, and then return search criteria and result to new table?

I have a set of records that need to be validated (searched) in a SQL table. I will call these ValData and SearchTable respectively. A colleague created a SQL query in which a record from the ValData can be copied and pasted in to a string variable, and then it is searched in the SearchTable. The best result from the SearchTable is returned. This works very well.
I want to automate this process. I loaded the ValData to SQL in a table like so:
RowID INT, FirstName, LastName, DOB, Date1, Date2, TextDescription.
I want to loop through this set of data, by RowID, and then create a result table that is the ValData joined with the best match from the SearchTable. Again, I already have a query that does that portion. I just need the loop portion, and my SQL skills are virtually non-existent.
Suedo code would be:
DECLARE #SearchID INT = 1
DECLARE #MaxSearchID INT = 15000
DECLARE #FName VARCHAR(50) = ''
DECLARE #FName VARCHAR(50) = ''
etc...
WHILE #SearchID <= #MaxSearchID
BEGIN
SET #FNAME = (SELECT [Fname] FROM ValData WHERE [RowID] = #SearchID)
SET #LNAME = (SELECT [Lname] FROM ValData WHERE [RowID] = #SearchID)
etc...
Do colleague's query, and then insert(?) search criteria joined with the result from the SearchTable in to a temporary result table.
END
SELECT * FROM FinalResultTable;
My biggest lack of knowledge comes in how do I create a temporary result table that is ValData's fields + SearchTable's fields, and during the loop iterations how do I add one row at a time to this temporary result table that includes the ValData joined with the result from the SearchTable?
If it helps, I'm using/wanting to join all fields from ValData and all fields from SearchTable.
Wouldn't this be far easier with a query like this..?
SELECT FNAME,
LNAME
FROM ValData
WHERE (FName = #Fname
OR LName = #Lname)
AND RowID <= #MaxSearchID
ORDER BY RowID ASC;
There is literally no reason to use a WHILE other than to destroy performance of the query.
With a bit more trial and error, I was able to answer what I was looking for (which, at its core, was creating a temp table and then inserting rows in to it).
CREATE TABLE #RESULTTABLE(
[feedname] VARCHAR(100),
...
[SCORE] INT,
[Max Score] INT,
[% Score] FLOAT(4),
[RowID] SMALLINT
)
SET #SearchID = 1
SET #MaxSearchID = (SELECT MAX([RowID]) FROM ValidationData
WHILE #SearchID <= #MaxSearchID
BEGIN
SET #FNAME = (SELECT [Fname] FROM ValidationData WHERE [RowID] = #SearchID)
...
--BEST MATCH QUERY HERE
--Select the "top" best match (order not guaranteed) in to the RESULTTABLE.
INSERT INTO #RESULTTABLE
SELECT TOP 1 *, #SearchID AS RowID
--INTO #RESULTTABLE
FROM #TABLE3
WHERE [% Score] IN (SELECT MAX([% Score]) FROM #TABLE3)
--Drop temp tables that were created/used during best match query.
DROP TABLE #TABLE1
DROP TABLE #TABLE2
DROP TABLE #TABLE3
SET #SearchID = #SearchID + 1
END;
--Join the data that was validated (searched) to the results that were found.
SELECT *
FROM ValidationData vd
LEFT JOIN #RESULTTABLE rt ON rt.[RowID] = vd.[RowID]
ORDER BY vd.[RowID]
DROP TABLE #RESULTTABLE
I know this could be approved by doing a join, probably with the "BEST MATCH QUERY" as an inner query. I am just not that skilled yet. This takes a manual process which took hours upon hours and shortens it to just an hour or so.

How to create another table or Temp table with the Pivot query results

I have this table
CREATE TABLE COMPUTERS
(
CUSTOMER_ID INT,
COMPUTER_NAME VARCHAR(50),
COMPUTER_OS VARCHAR(50)
);
INSERT INTO COMPUTERS
VALUES (15001, 'DESKTOP-JKVB','Windows 7'),
(15001, 'DESKTOP-SKVB','Windows 2012R2'),
(15002, 'PC-JKVB45','Windows VISTA'),
(15002, 'JOHN-PC','Windows 10'),
(15002, 'SERVER-DC','Windows 7'),
(15002, 'DATA-PC','Windows 2016'),
(15002, 'PC-BACKOFFICE','Windows 2008R2'),
(15003, 'DESKTOP-XPBACK','Windows 7'),
(15003, 'PC-HDFU','Windows 2012R2'),
(15003, 'DESKTOP-NO2','Windows 10'),
(15004, 'SERVER-DHCP','Windows 7'),
(15004, 'DESKTOP-NO1','Windows 2012R2');
With the help of Stack overflow , i have written a query to pivot the above data.
My question is whether we can create a temp table or normal table with result of the pivot data.
PIVOT QUERY
;WITH cte AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY Customer_ID ORDER BY Customer_ID) AS Number
FROM computers
),
cte2 AS
(
SELECT DISTINCT
c.Customer_ID,
c.Computer_Name,
'Computer_Name_' + CAST(Number AS VARCHAR(100)) AS Computer
FROM
cte AS c
),
cte_dist2 AS
(
SELECT DISTINCT Customer_ID
FROM computers
),
cte3 AS
(
SELECT
c.Customer_ID, c.Computer_OS,
'Computer_OS_' + CAST(Number AS VARCHAR(100)) AS OS
FROM
cte AS c
)
SELECT DISTINCT
cd2.Customer_ID,
MAX(IIF(c2.Computer='Computer_Name_1',c2.Computer_Name,NULL)) as Computer_Name_1,
MAX(IIF(c3.OS='Computer_OS_1',c3.Computer_OS,NULL)) as Computer_OS_1,
MAX(IIF(c2.Computer='Computer_Name_2',c2.Computer_Name,NULL)) as Computer_Name_2,
MAX(IIF(c3.OS='Computer_OS_2',c3.Computer_OS,NULL)) as Computer_OS_2,
MAX(IIF(c2.Computer='Computer_Name_3',c2.Computer_Name,NULL)) as Computer_Name_3,
MAX(IIF(c3.OS='Computer_OS_3',c3.Computer_OS,NULL)) as Computer_OS_3,
MAX(IIF(c2.Computer='Computer_Name_4',c2.Computer_Name,NULL)) as Computer_Name_4,
MAX(IIF(c3.OS='Computer_OS_4',c3.Computer_OS,NULL)) as Computer_OS_4,
MAX(IIF(c2.Computer='Computer_Name_5',c2.Computer_Name,NULL)) as Computer_Name_5,
MAX(IIF(c3.OS='Computer_OS_5',c3.Computer_OS,NULL)) as Computer_OS_5
FROM
cte_dist2 AS cd2
INNER JOIN
cte2 AS c2 ON cd2.Customer_ID = c2.Customer_ID
INNER JOIN
cte3 AS c3 ON cd2.Customer_ID = c3.Customer_ID
GROUP BY
cd2.Customer_ID
Yes you can use temp tables below show two methods that you can use for creating temp tables
select * into #temp from (--select query of the final cte function
or query) and temp table will be automatically create for you.
If you want to create your own temp table create temp table like below.
Eg
create table #Temp
(
EventID int,
EventTitle Varchar(50),
EventStartDate DateTime,
EventEndDate DatetIme,
EventEnumDays int,
EventStartTime Datetime,
EventEndTime DateTime,
EventRecurring Bit,
EventType int
)
and use Insert Into #Temp --select query of the final cte function
or query will also work for you.

Delete entries of a specific time range

I want to delete from an existing table the entries that respond to a specific time range if they exist in this table, then calculate them and insert them to it.
If they do not exist create them and just insert to the table.
How is that possible to be done?
Could anyone give an example of this?
The table structure should be the following:
create table test_table
(
[Date] float
,[UserName] nvarchar(max)
,[SessionType] int
,[Duration] float
,[MessageCount] int
)
if you have a Column that Stores the DATE and TIME then you can just delete the records based on that.
Suppose I have a Column Called CreateDate on My table. Then I can Delete all records Created between 10.00 am and 11.00 Today by Just Giving
DELETE FROM MyTable WHERE
CreateDate BETWEEN '2018-01-12 10:00:00.000' AND '2018-01-12 11:00:00.000'
Now Insert the values again using the Normal INSERT statement
You can do in steps like this
First store the set of records in time range in a temp table
SELECT * INTO tempTable FROM YourTable WHERE CONVERT(FLOAT, [Date]) BETWEEN 43100.3603763503 AND 43110.3603763503
Then delete the records from the table
DELETE FROM YourTable WHERE CONVERT(FLOAT, [Date]) BETWEEN 43100.3603763503 AND 43110.3603763503
Then do the calculations as per your requirement with the data available in tempTable and insert the data into your table
INSERT INTO YourTable
SELECT * FROM tempTable
Then drop the tempTable
DROP TABLE tempTable
I am updating my solution to this thank you all for help.
create mytable table
(
[Date] float
,[UserName] nvarchar(max)
,[SessionType] int
,[Duration] float
,[MessageCount] int
)
IF EXISTS (SELECT * FROM mytable WHERE [Date] >= 43082 AND [Date] < 43111)
BEGIN
DELETE FROM mytable WHERE [Date] >= 43082 AND [Date]< 43111
;WITH Data AS (
/*do the calculation of data*/
)
INSERT INTO mytable select * from Data
END
ELSE
BEGIN
;WITH Data AS (
/*do the calculation of data*/
)
INSERT INTO mytable select * from Data
END

Need T-SQL Query for Groups and Islands

create table #sample (rowguid int identity ,id_frm int ,id_to int)
insert into #sample values( 1,5)
insert into #sample values( 7,13)
insert into #sample values( 17,20)
In the above table I have values starting Id and Ending Id. I need to prepare a table which has all the numbers falls between starting Id and Ending Id
i have tried it with looping but response is very slow in real world.
any body help me with query ???
This is what I have tried so far...
declare #sql varchar(8000) = '
select top '+cast((select max(id_to) from #sample) as varchar(100))+' identity(int,1,1) as guidid into tally from sysobjects,syscolumns '
exec (#sql)
alter table Tally add slab varchar(10)
create clustered index idx on Tally(guidid)
create clustered index idx on #sample(id_frm asc,id_to desc)
update Tally set slab = rowguid
from #sample join Tally on guidid between id_frm and id_to
delete from Tally where slab is null
select * from Tally
This query works fine with small numbers
But My real time table have 13 digit nos. It through Arithmetic overflow error
Assuming the range id_frm and id_to is relatively small integers, e.g. < 1M, one technique to approach this problem is to create a table with all values in the range and join to it:
WITH lotsOfNumbers AS
(
SELECT ROW_NUMBER() OVER (ORDER BY o1.object_id) AS id
FROM sys.objects o1 CROSS JOIN sys.objects o2
)
INSERT INTO #targetTable
SELECT l.ID
FROM lotsOfNumbers l
INNER JOIN #sample
ON l.ID BETWEEN #sample.id_frm AND #sample.id_to;
SqlFiddle here
A permanent table with the necessary range of ID's and a clustered index on the ID would improve performance, obviously.
Add in a DISTINCT if your ranges overlap, and you don't want duplicates in the result.
If you are able to get a full range of acceptable values into another table, you can use it without looping. The meathod below gets the minimum (1) and maximum (20), and the temporary table named #range will return everything in between.
drop table #sample
drop table #range
create table #sample (id_frm int ,id_to int)
insert into #sample values( 1,5)
insert into #sample values( 7,13)
insert into #sample values( 17,20)
create table #range (id int)
insert into #range select 1
go
insert into #range select top 1 max(id)+ 1 from #range
go 100
declare #min int
declare #max int
set #min= (select min(id_frm ) from #sample )
set #max = (select max(id_to) from #sample )
select * from #range where id between #min and #max

Resources