Updating 20 rows in a table is really slow - sql

I can't figure out why updating only 21 rows in a table takes so much time.
Step 1: I'm creating #tempTable from the StagingTable (it will never have more than 20 rows of data)
CREATE TABLE #tmpTable (
ID INT NULL,
UniqueID INT NULL,
ReportDate VARCHAR(15) NULL,
DOB Datetime NULL,
Weight VARCHAR(15) NULL,
Height VARCHAR(15) NULL)
INSERT INTO #tempTable (
ID,
UniqueID,
ReportDate,
DOB,
Weight,
Height)
SELECT
A.ID,
A.UniqueID,
A.ReportDate,
A.DOB,
A.Weight,
A.Height
FROM [testDB].[StagingTable] as A
WHERE A.UniqueID = '12345'
Step 2. Updating FinalTable:
UPDATE [Customers].[FinalTable]
SET ID = B.ID,
UniqueID = B.UniqueID,
ReportDate = B.ReportDate,
DOB = B.DOB,
Weight = B.Weight,
Height = B.Height
FROM #tempTable AS B
WHERE [Customers].[FinalTable].[ReportDate] = B.ReportDate
AND [Customers].[FinalTable].[DOB] = B.DOB
This query takes more than 30 minutes!
Is there any way to speed up this update process? Any ideas what I might be doing wrong?
I just want to add that the FinalTable has millions of rows...
Any help would be greatly appreciated.
Thanks!

If there are only 30 matches, then you want an index on #temptable(ReportDate, DOB):
create index idx_temptable_2 on #temptable(ReportDate, DOB);

Related

Sybase ASE fetching millions of rows is slow

A query on Sybase tables takes a long time to fetch when the returned row count is in the millions. Execution is fast with indexes, but fetch is slow. What are some ideas to improve the performance of the fetch?
The three tables are similar to:
CREATE TABLE Table1{ // data
commonCol1 varchar(50),
table1Col1 float,
table1Col2 varchar(50),
table1Col3 int default 0,
commonCol3 DATE,
PRIMARY KEY (commonCol1, commonCol3)
}
CREATE INDEX index1 ON Table1(commonCol1, table1Col1, table1Col2, table1Col3, commonCol3)
CREATE TABLE Table2{ // dep
commonCol2 varchar(50),
table2Col1 int default 0,
commonCol3 DATE,
PRIMARY KEY (commonCol2, commonCol3)
}
CREATE INDEX index2 ON Table2(commonCol2, table2Col1, commonCol3)
CREATE TABLE Table3 { // join
commonCol1 varchar(50),
commonCol2 varchar(50),
commonCol3 DATE,
PRIMARY KEY (commonCol1, commonCol2, commonCol3)
}
CREATE INDEX index3 ON Table3(commonCol1, commonCol2, commonCol3)
The query is:
CREATE PROCEDURE dbo.myProc #val1 varchar(50), #val2 varchar(50)
AS BEGIN
SELECT Table3.commonCol1, Table1.table1Col1, Table1.table1Col3 FROM Table3
INNER JOIN Table1 ON Table1.commonCol1 = Table3.commonCol1
INNER JOIN Table2 ON Table2.commonCol2 = Table3.commonCol2
WHERE Table1.table1Col2 = #val1 AND Table1.commonCol3 = CONVERT(DATE, #val2) AND Table2.commonCol3 = CONVERT(DATE, #val2) AND Table3.commonCol3 = CONVERT(DATE, #val2)
GROUP BY Table3.commonCol1, Table1.table1Col1, Table1.table1Col3
HAVING MIN( Table2.table2Col1 ) = 1 AND ( MAX( Table1.table1Col3 ) <=1 OR MIN( ABS ( ROUND( Table1.table1Col1, 4) ) ) > 0 )
END

An aggregate may not appear in the set list of an UPDATE statement T-SQL

In T-SQL I'm attempting to update a stock user field with the number of weeks we expect it to be delivered to us by taking the difference between today and the purchase order due in dates. However the select query can return more than one line of purchase orders if there is more than one purchase order containing that product (obviously). I would like to take the smallest number it returns / minimum value but obviously cannot do this within the update query. Can anyone recommend a workaround? Thanks.
UPDATE [Exchequer].[ASAP01].[STOCK]
SET stUserField7 = DATEDIFF(day,CONVERT(VARCHAR(8), GETDATE(), 112),min(tlLineDate)) / 7 + 1
FROM [Exchequer].[ASAP01].[STOCK]
JOIN [Exchequer].[ASAP01].[CUSTSUPP]
ON stSupplier = acCode
JOIN [Exchequer].[ASAP01].[DETAILS]
ON stCode = tlStockCodeTrans1
WHERE stSupplier <> '' AND stQtyOnOrder > '0' AND stQtyOnOrder > stQtyAllocated
AND tlOurRef like 'POR%' AND (floor(tlQtyDel) + floor(tlQtyWOFF)) < floor(tlQty)
AND tlLineDate >= CONVERT(VARCHAR(8),GETDATE(), 112)
Why are you casting date to varchar for the difference?
This is not date but how you can use a window function in an update
declare #maps table(name varchar(10), isUsed bit, code varchar(10));
insert into #Maps values
('NY', 1, 'NY1')
, ('NY', 0, 'NY2')
, ('FL', 0, 'FL1')
, ('TX', 0, 'TX1')
declare #Results table (id int identity primary key, Name varchar(20), Value int, Code varchar(20), cnt int)
insert into #results values
('FL', 12, 'FL1', null)
, ('TX', 54, 'TX1', null)
, ('TX', 56, 'TX1', null)
, ('CA', 50, 'CA1', null)
, ('NJ', 40, 'NJ1', null)
select * from #results
order by name, Value desc
update r
set r.cnt = tt.cnt
from #results r
join ( select id, max(value) over (partition by name) as cnt
from #Results
) tt
on r.id = tt.id
select * from #results
order by name, value desc
Build a SELECT query with columns for the following:
The primary key of the [Exchequer].[ASAP01].[STOCK] table
The new desired stUserField7
Given the MIN(tlLineDate) expression in the original question, if this SELECT query does not have either a GROUP BY clause or change to use an APPLY instead of a JOIN, you've probably done something wrong.
Once you have that query, use it in an UPDATE statement like this:
UPDATE s
SET s.stUserField7 = t.NewValueFromSelectQuery
FROM [Exchequer].[ASAP01].[STOCK] s
INNER JOIN (
--- your new SELECT query here
) t ON t.<primary key field(s)> = s.<primary key field(s)>

performance impact of making where clause dummy - SQL Server

I need to know about the performance impact of the below method of writing the query.
Assume there is an employee table. Requirement is to get a list of employees under a particular department and optionally the user can filter the result set by providing the city/location.
declare #dept varchar(10) = 'ABC', #city varchar(10)
select * from employee where dept = #dept and city = isnull(#city, city)
Is this fine? or do we need to use traditional if logic to check whether the user provided city as input?
Thanks,
Sabarish.
I remember reading somewhere that the following syntax is quicker than calling ISNULL():
select * from employee where dept = #dept and (#city IS NULL OR #city = city)
It was something to do with the SQL compiler effectively knowing that it can ignore the expression in brackets if #city is null.
Sorry but no idea where I read this (it was some time ago), otherwise I would cite it properly.
Most powerfull aproach to solve performance problems with nulls is try to avoid nulls by default values. In your case should be good try something like:
declare #dept varchar(10) = 'ABC', #city varchar(10) = 'unknown'
SELECT *
FROM employee
WHERE dept = #dept AND
#city = 'unknown'
UNION
SELECT *
FROM employee
WHERE dept = #dept AND
city = #city AND
#city != 'unknown'
Why?
Cardinality estimator is not able to estimate correct number of rows that query returns and it causes, that execution plan should be bad for this particular query. Avoid nulls and everything will be great B-)
For sure the answer provided by #Jonathan will improve performance if 'City' column has separate NonClustered Index on it. If not both the execution plan will lead to SCAN. If you have NonClustered Index then the Jonathan's approach will do SEEK instead of SCAN which will be good in terms of performance.
Let me try to explain why that is the scenario with a sample as in below table: For ease of use I did not considered two predicates dept and city instead I am considering only City.
Consider below Employee table:
CREATE TABLE [dbo].[Employee](
[EmployeeId] [int] NULL,
[EmployeeName] [varchar](20) NULL,
[Dept] [varchar](15) NULL,
[city] [varchar](15) NULL
) ON [PRIMARY]
GO
--Creating Clustered Index on Id
CREATE CLUSTERED INDEX [CI_Employee_EmployeeId] ON [dbo].[Employee] ( [EmployeeId] ASC)
--Loading Data
Loading Sample data
Insert into Employee
Select top (10000) EmployeeId = Row_Number() over (order by (Select NULL))
,EmployeeName = Concat ('Name ',Row_Number() over (order by (Select NULL)))
,Dept = Concat ('Dept ',(Row_Number() over (order by (Select NULL))) % 50)
,City = Concat ('City ',Row_Number() over (order by (Select NULL)))
from master..spt_values s1, master..spt_values s2
Now Executing simple query with normal predicate:
Declare #city varchar(15) = 'City 1500'
Select * from Employee where city = #city
--It Does Clustered Index Scan
Now creating an non-clustered Index on city
--Now adding Index on City
Create NonClustered Index NCI_Employee_City on dbo.Employee (city)
Declare #city varchar(15) = 'City 1500'
Select * from Employee where city = #city
--It Does Index Seek
Now coming to your isnull function
Since it forces function on each city it uses SCAN as below
Declare #city varchar(15) = 'City 1500'
Select * from Employee where city = isnull(#city, City)
go
Declare #city varchar(15) = 'City 1500'
Select * from Employee where city is null or city = #city
If you look at the overall percentage it takes more for IsNull function.
So if you have an Index all these will be helpfull else it is going to be scan anyway.

Update: subquery returned more than 1 value

I am having a problem, and I can't figure out how to fix this query. I have a temp table, one of the columns should contain a calculated value of another column divided by a sum of groups of that column. I don't know how to write this so that I avoid the error.
Declare #Temp Table
(
ZipCode char(5) Not Null,
StateFacilityId varchar (50) Not Null,
Cnt int Not Null,
MarketShare float,
Row int Not Null,
Primary Key Clustered (ZipCode, StateFacilityId)
);
Insert Into #Temp (ZipCode, StateFacilityId, Cnt, Row)
Select d.ZipCode, d.StateFacilityId, Cnt = COUNT(*), Row = ROW_NUMBER()OVER(PARTITION BY ZipCode ORDER BY Count(*) DESC)
From [MarketShareIQData].[dbo].[tblServicesDetail] d
Group By d.ZipCode, d.StateFacilityId
;
Update #Temp
Set MarketShare =(h.Cnt/(
Select SUM(h.Cnt)
From #Temp h
Group By ZipCode
))
From #Temp h
A group by would return one row per group. I'm guessing you're looking for the single group with matching zipcode. You could do that like:
update h
set MarketShare = h.Cnt /
(
select sum(h2.Cnt)
from #Temp h2
where h2.ZipCode = h.ZipCode
)
from #Temp h

SQL query Optimization help

I have the the following SQL query
Declare #tempcalctbl Table
(
ItemId varchar(50),
ItemLocation varchar(50),
ItemNo varchar(50),
Width real,
Unit varchar(50),
date datetime
)
Insert Into #tempcalctbl
Select distinct SubId,ItemLocation,ItemNo,
(ABS((Select width From #temptbl a Where ItemProcess ='P1'and a.ItemId = c.ItemId
and a.ItemNo = c.ItemNo and a.ItemLocation = c.ItemLocation)
-(Select width From #temptbl b Where ItemProcess ='P2' and b.ItemId = c.ItemId
and b.ItemNo = c.ItemNo and b.ItemLocation = c.ItemLocation))) * 1000,
Unit,date
From #temptbl c
Group by ItemId,ItemLocation,ItemNo,Unit,date
I was wondering how to optimize this query.
The idea is to find out the different in width (p1's item - p2's item) between ItemProcess 'P1' and 'P2' according to the same ItemID, same ItemNo and same ItemLocation.
I have around 75000 and it took more then 25 minute to get the width differences for all the ItemId.
I tried to use Group by for the width different calculation but it would return multiple row instead of just a value which then would return error. By the way I am use MS SQL server 2008 and #tempcalctbl is a table that I declared in a store procedure.
Does the following help?
INSERT INTO #tempcalctbl
SELECT P1.SubId ,
P1.ItemLocation ,
P1.ItemNo ,
ABS(P1.Width - P2.Width) * 1000 AS Width ,
P1.Unit ,
P1.date
FROM #temptbl AS P1
INNER JOIN #temptbl AS P2 ON P1.ItemId = P2.ItemId
AND P1.ItemNo = P2.ItemNo
AND P1.ItemLocation = P2.ItemLocation
WHERE P1.ItemProcess = 'P1'
AND P2.ItemProcess = 'P2'
EDIT
To make use of indexes, you will need to change your table variable to a temporary table
CREATE TABLE #temptbl
(
ItemId varchar(50),
ItemLocation varchar(50),
ItemNo varchar(50),
Width real,
Unit varchar(50),
date DATETIME,
ItemProcess INT,
SubId INT
)
CREATE NONCLUSTERED INDEX Index01 ON #temptbl
(
ItemProcess ASC,
ItemId ASC,
ItemLocation ASC,
ItemNo ASC
)
INCLUDE ( SubId,Width,Unit,date)
GO
That should speed you up a little.
John Petrak's answer is the best query for this case.
If the speed is still now acceptable, maybe you can store #temptbl at a temporary real table, and create the related index on those four columns.