I am new to sql tuning. I have the following SQL which takes around 15 to 20 seconds to produce the results.
SELECT D.DealerName,
Z.Zone,
C.Id ,
L.Id ,
A.Id ,
L.LeadDate,
LT.LeadType ,
EM.FirstName + ' ' + EM.LastName ,
LS.LeadSource ,
--C.*,
E.Id ,
E.StartDateTime,
0 ,
Chiefed = CASE A.AppointmentTypeId
WHEN 3 THEN 'True'
ELSE ''
END,
9 AS WorkflowPhase
FROM Customers C( NOLOCK )
INNER JOIN Dealers D
ON C.DEALERId = D.Id
INNER JOIN Leads L( NOLOCK )
ON L.CustomerId = C.Id
INNER JOIN Appointments A( NOLOCK )
ON A.LeadId = L.Id
AND ( NOT( A.AppointmentTypeId = 5
OR A.AppointmentTypeId = 6 ) )
JOIN CalendarEvents E( NOLOCK )
ON E.TableId = 1
AND E.TableRowId = A.Id
AND E.IsDeleted = 0
AND Dateadd(hh, #TZO, Getdate()) >= E.StartDateTime
LEFT OUTER JOIN AppointmentResults AR( NOLOCK )
ON AR.EventId = E.Id
LEFT OUTER JOIN LeadSources LS( NOLOCK )
ON LS.Id = L.LeadSourceId
LEFT OUTER JOIN LeadTypes LT( NOLOCK )
ON LT.Id = L.LeadTypeId
LEFT OUTER JOIN Users EM( NOLOCK )
ON EM.Id = E.EmployeeId
LEFT OUTER JOIN Zone Z( NOLOCK )
ON Z.Id = C.ZoneId
WHERE EXISTS(SELECT 1
FROM WorkflowStatus WS( NOLOCK )
WHERE TableId = 1
AND TableRowId = A.Id
AND WorkflowPhaseId = 9
AND IsCompleted = 0
AND IsDeleted = 0)
AND ( EXISTS (SELECT 1
FROM dbo.Uft_userpermissionzonesbyworkflow(#EmployeeId, 9)
WHERE ZoneId = C.zoneid) )
AND EXISTS (SELECT 1
FROM Uft_userenableddealers(#EmployeeId)
WHERE DealerId = C.DealerId)
ORDER BY C.LastName,
C.CompanyName,
C.CompanyContact
I already tuned up to my knowledge but still I can see some index scans. I tried to convert those index scans to index seek but it is not possible due to number of records.
Please refer the screenshot of plan diagram and top operations
Kindly provide any suggestions to improvise this query.
DECLARE #p TABLE (DealerId INT PRIMARY KEY WITH (IGNORE_DUP_KEY=ON))
INSERT INTO #p
SELECT DealerId
FROM dbo.Uft_userenableddealers(#EmployeeId)
DECLARE #z TABLE (ZoneId INT PRIMARY KEY WITH (IGNORE_DUP_KEY=ON))
INSERT INTO #z
SELECT ZoneId
FROM dbo.Uft_userpermissionzonesbyworkflow(#EmployeeId, 9)
SELECT ...
FROM ...
WHERE EXISTS(SELECT 1
FROM WorkflowStatus WS( NOLOCK )
WHERE TableId = 1
AND TableRowId = A.Id
AND WorkflowPhaseId = 9
AND IsCompleted = 0
AND IsDeleted = 0)
AND C.zoneid IN (SELECT * FROM #z)
AND C.DealerId IN (SELECT * FROM #p)
ORDER BY C.LastName,
C.CompanyName,
C.CompanyContact
OPTION(RECOMPILE)
as discussed below Devarts answer here the example with a CTE instead of the declared table variables. I'd assume that the declared TVs are faster due to the key, but the CTE is ad-hoc and - maybe - better integrated. Thx for testing:
;WITH p AS
(
SELECT DealerId
FROM dbo.Uft_userenableddealers(#EmployeeId)
)
,z AS
(
SELECT ZoneId
FROM dbo.Uft_userpermissionzonesbyworkflow(#EmployeeId, 9)
)
SELECT ...
FROM ...
WHERE EXISTS(SELECT 1
FROM WorkflowStatus WS( NOLOCK )
WHERE TableId = 1
AND TableRowId = A.Id
AND WorkflowPhaseId = 9
AND IsCompleted = 0
AND IsDeleted = 0)
AND C.zoneid IN (SELECT ZoneId FROM z)
AND C.DealerId IN (SELECT DealerId FROM p)
ORDER BY C.LastName,
C.CompanyName,
C.CompanyContact
OPTION(RECOMPILE)
Related
I have a select query which utilizes UNION ALL keyword on two tables with same structure (columns and primary key, they have different non-clustered indexes). These two tables contain 39 million rows, a million in one and a 38 million in the other. When running a query just on a table1 which has a million rows, it takes approximately 0.2 seconds, on table2 we have a different situation it takes from 0.5 to 1.2 seconds maximum, depending on the stress of DB.
In reality, for displaying I need to union those two tables, but the problem is that the union query takes a whopping 8 seconds to run. When taking a look at execution plan, the most heavy operation is Merge Join (Concatenation) with the cost of 91%, I am little bit concerned as the WHERE clause I'm running selects 51 entries from table1 and 0 entries from the table2 (larger table).
I can't get my head around it, I've been trying to find any solutions to my problem for last two days now and all I found was either UNION being used instead of UNION ALL or unnecessary clauses being in place like GROUP BY or LEFT/INNER JOINS. Query also does paging, using this commands ORDER BY [Id] DESC OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY;, all tests (on single tables and with UNION ALL) were performed with paging (OFFSET and FETCH NEXT) keywords in place.
If required, I can provide the table details and query details. It a simple select query with 2 INNER JOINS and 2 LEFT JOINS all of the joined tables contain really small amount of data (ranging from 50 entries to 20K entries).
Here's the query
SELECT *
FROM (SELECT tr.Id,
tr.Amount,
tr.TypeId,
t.Name AS [Type],
tr.Date,
tr.ExternalKey,
tr.ExternalDescription,
tr.GameId,
tr.GameProviderId,
gp.Name AS GameProvider,
u.Username,
u.Pincode,
gp.Name,
g.GameName,
u.OperatorId,
tr.BalanceBefore,
tr.BalanceAfter,
tr.UserId
FROM (
SELECT *
FROM dbo.ActiveTransactions at
WHERE ( 1 = 1 )
AND ( [Date] >= '2017-07-17 20:00:00' )
AND ( [TypeId] != 10 )
AND ( [UserId] = 29041 )
UNION ALL
SELECT *
FROM dbo.TransactionHistory th --WITH(INDEX(IX_TransactionHistory_DateType_UserId))
WHERE ( 1 = 1 )
AND ( [Date] >= '2017-07-17 20:00:00' )
AND ( [TypeId] != 10 )
AND ( [UserId] = 29041 )
) AS tr
INNER JOIN dbo.Users u ON tr.UserId = u.Id
LEFT JOIN dbo.GameProviders gp ON tr.GameProviderId = gp.Id
LEFT JOIN dbo.Games g ON tr.GameId = g.GameId AND tr.GameProviderId = g.ProviderId
INNER JOIN dbo.Types t ON tr.TypeId = t.Id ) AS t
ORDER BY [Id] DESC OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY;
This is an educated guess: without knowledge of indexes or execution plan on what the engine is actually getting caught up on.
Consider: Doing the union after the joins/filters instead of before: you have to repeat the joins but you may gain some index efficiency's lost in a unioned set processing.
In this case I created two CTE's and then unioned them.
as I'm unable to really test this, I may have some syntax errors.
WITH cte1 AS
(SELECT tr.Id,
tr.Amount,
tr.TypeId,
t.Name AS [Type],
tr.Date,
tr.ExternalKey,
tr.ExternalDescription,
tr.GameId,
tr.GameProviderId,
gp.Name AS GameProvider,
u.Username,
u.Pincode,
gp.Name,
g.GameName,
u.OperatorId,
tr.BalanceBefore,
tr.BalanceAfter,
tr.UserId
FROM (
SELECT *
FROM dbo.ActiveTransactions at
WHERE ( 1 = 1 )
AND ( [Date] >= '2017-07-17 20:00:00' )
AND ( [TypeId] != 10 )
AND ( [UserId] = 29041 )) AS tr
INNER JOIN dbo.Users u ON tr.UserId = u.Id
LEFT JOIN dbo.GameProviders gp ON tr.GameProviderId = gp.Id
LEFT JOIN dbo.Games g ON tr.GameId = g.GameId AND tr.GameProviderId = g.ProviderId
INNER JOIN dbo.Types t ON tr.TypeId = t.Id ) AS t),
CTE2 as (
SELECT tr.Id,
tr.Amount,
tr.TypeId,
t.Name AS [Type],
tr.Date,
tr.ExternalKey,
tr.ExternalDescription,
tr.GameId,
tr.GameProviderId,
gp.Name AS GameProvider,
u.Username,
u.Pincode,
gp.Name,
g.GameName,
u.OperatorId,
tr.BalanceBefore,
tr.BalanceAfter,
tr.UserId
FROM (SELECT *
FROM dbo.TransactionHistory th --WITH(INDEX(IX_TransactionHistory_DateType_UserId))
WHERE ( 1 = 1 )
AND ( [Date] >= '2017-07-17 20:00:00' )
AND ( [TypeId] != 10 )
AND ( [UserId] = 29041 ) as tr
INNER JOIN dbo.Users u ON tr.UserId = u.Id
LEFT JOIN dbo.GameProviders gp ON tr.GameProviderId = gp.Id
LEFT JOIN dbo.Games g ON tr.GameId = g.GameId AND tr.GameProviderId = g.ProviderId
INNER JOIN dbo.Types t ON tr.TypeId = t.Id) AS t
)
SELECT * from CTE1
UNION ALL
SELECT * from CTE2
ORDER BY [Id] DESC OFFSET 0 ROWS FETCH NEXT 25 ROWS ONLY;
I am having a query in which number of execution is equal to the number of rows in the table.
Anyone knows any possible reason for that as a result the clustered index seek %age is very high.
I have update the Stats for the Tables.But still no benefit.
select #AdditionalBillToNumericValue = cast(ab_vnum as int) from #table1
inner join table4 on ab_btid = ay_bt and ab_type = 'SRVGRP'
if(#AdditionalBillToNumericValue is not null or #AdditionalBillToNumericValue <> '')
begin
select distinct
fs.sv_pkey
, fs.sv_id
, fs.sv_desc
,ISNULL( fs.sv_ehr,0) as sv_ehr
,ISNULL( fs.sv_emn,0) as sv_emn
,ISNULL( fs.sv_lhr,0) as sv_lhr
,ISNULL( fs.sv_lmn,0) as sv_lmn
,(case when #table1.fh_sv = fs.sv_id then 1 else 0 end) IsDefault
from table3 fr
inner join table4 ab on ab.ab_vnum = fr.sr_sgpkey
inner join table5 fs on fr.sr_svpkey = fs.sv_pkey or fs.sv_pkey = #ServiceWindowKey
inner join #table1 on ab.ab_btid = #table1.ay_bt
AND ab.ab_type = 'SRVGRP'
end
else
begin
select
fs.sv_pkey
, fs.sv_id
, fs.sv_desc
,ISNULL(sv_ehr,0) as sv_ehr
,ISNULL(sv_emn,0) as sv_emn
,ISNULL(sv_lhr,0) as sv_lhr
,ISNULL(sv_lmn,0) as sv_lmn
,(case when #table1.fh_sv = fs.sv_id then 1 else 0 end) IsDefault
from table3 fr
inner join table4 ab on ab.ab_vnum = fr.sr_sgpkey
inner join table5 fs on fr.sr_svpkey = fs.sv_pkey or fs.sv_pkey = #ServiceWindowKey
LEFT join #table1 with(nolock) on ab.ab_btid = #table1.ay_bt
where ab.ab_type = 'SRVGRP'
Please Verify where we are having the join with table5 it is getting the issue.
In Join Both are having int datatype.
Hi all is there any way that i can use multiple values in between clause as
column_name between 0 and 100 or 200 and 300 like this
Any help would be appreciated
here is my query SELECT CASE WHEN ISNUMERIC(value_text) = 1 THEN CAST(value_text AS INT) ELSE -1 END) between 0 and 100
i just want to append multiple values in between clause
This is full query
SELECT ROW_NUMBER() OVER
(
order by Vendor_PrimaryInfo.Vendor_ID asc
)AS RowNumber
, Unit_Table.Unit_title, Vendor_Base_Price.Base_Price, Vendor_Base_Price.showprice, Category_Table.Title, Vendor_Registration.Business_Name,
Vendor_PrimaryInfo.Street_Address, Vendor_PrimaryInfo.Locality, Vendor_PrimaryInfo.Nearest_Landmark, Vendor_PrimaryInfo.City, Vendor_PrimaryInfo.State,
Vendor_PrimaryInfo.Country, Vendor_PrimaryInfo.PostalCode, Vendor_PrimaryInfo.Latitude, Vendor_PrimaryInfo.Longitude, Vendor_PrimaryInfo.ImageUrl,
Vendor_PrimaryInfo.ContactNo, Vendor_PrimaryInfo.Email,Vendor_PrimaryInfo.Vendor_ID
FROM Unit_Table INNER JOIN
Vendor_Base_Price ON Unit_Table.Unit_ID = Vendor_Base_Price.Unit_ID INNER JOIN
Vendor_PrimaryInfo ON Vendor_Base_Price.Vendor_ID = Vendor_PrimaryInfo.Vendor_ID INNER JOIN
Vendor_Registration ON Vendor_Base_Price.Vendor_ID = Vendor_Registration.Vendor_ID AND
Vendor_PrimaryInfo.Vendor_ID = Vendor_Registration.Vendor_ID INNER JOIN
Category_Table ON Vendor_Registration.Category_ID = Category_Table.Category_ID
LEFT JOIN
Vendor_Value_Table ON Vendor_Registration.Vendor_ID = Vendor_Value_Table.Vendor_ID LEFT JOIN
Feature_Table ON Vendor_Value_Table.Feature_ID = Feature_Table.Feature_ID
where Vendor_Registration.Category_ID=5 and Vendor_PrimaryInfo.City='City'
AND(
value_text in('Dhol Wala$Shahnai Wala')
or
(SELECT CASE WHEN ISNUMERIC(value_text) = 1 THEN CAST(value_text AS INT) ELSE -1 END) between 0 and 100
)
You can do this using AND/OR logic
value_text NOT LIKE '%[^0-9]%' and
(
value_text between 0 and 100
Or
value_text between 101 and 200
)
If you don't want to repeat the column name then frame the range in table valued constructor and join with your table
SELECT Row_number()
OVER (
ORDER BY Vendor_PrimaryInfo.Vendor_ID ASC )AS RowNumber,
Unit_Table.Unit_title,
Vendor_Base_Price.Base_Price,
Vendor_Base_Price.showprice,
Category_Table.Title,
Vendor_Registration.Business_Name,
Vendor_PrimaryInfo.Street_Address,
Vendor_PrimaryInfo.Locality,
Vendor_PrimaryInfo.Nearest_Landmark,
Vendor_PrimaryInfo.City,
Vendor_PrimaryInfo.State,
Vendor_PrimaryInfo.Country,
Vendor_PrimaryInfo.PostalCode,
Vendor_PrimaryInfo.Latitude,
Vendor_PrimaryInfo.Longitude,
Vendor_PrimaryInfo.ImageUrl,
Vendor_PrimaryInfo.ContactNo,
Vendor_PrimaryInfo.Email,
Vendor_PrimaryInfo.Vendor_ID
FROM Unit_Table
INNER JOIN Vendor_Base_Price
ON Unit_Table.Unit_ID = Vendor_Base_Price.Unit_ID
INNER JOIN Vendor_PrimaryInfo
ON Vendor_Base_Price.Vendor_ID = Vendor_PrimaryInfo.Vendor_ID
INNER JOIN Vendor_Registration
ON Vendor_Base_Price.Vendor_ID = Vendor_Registration.Vendor_ID
AND Vendor_PrimaryInfo.Vendor_ID = Vendor_Registration.Vendor_ID
INNER JOIN Category_Table
ON Vendor_Registration.Category_ID = Category_Table.Category_ID
LEFT JOIN Vendor_Value_Table
ON Vendor_Registration.Vendor_ID = Vendor_Value_Table.Vendor_ID
LEFT JOIN Feature_Table
ON Vendor_Value_Table.Feature_ID = Feature_Table.Feature_ID
JOIN (VALUES (0, 100),
(101, 200),
(201, 300)) tc (st, ed)
ON Try_cast(value_text AS INT) BETWEEN st AND ed
OR Try_cast(value_text AS VARCHAR(100)) = 'Dhol Wala$Shahnai Wala'
WHERE Vendor_Registration.Category_ID = 5
AND Vendor_PrimaryInfo.City = 'City'
Note : You have stored two different information's in a single column which causes lot of pain when you want to extract the data like this. Consider changing your table structure
I have nearly 3 million records in a table. when i select the records from using the below query it takes atleast 45 secs.
SELECT a.user_id,
b.NAME,
a.paid_date,
(SELECT Count(user_id)
FROM tbl_payment_master
WHERE user_id = a.user_id
AND paid_date = a.paid_date
GROUP BY paid_date) AS noofpaymnt,
(SELECT Count(user_id)
FROM tbl_payment_master
WHERE user_id = a.user_id
AND Month(paid_date) = Month(a.paid_date)
AND Year(paid_date) = Year(a.paid_date)) AS noofpaymonth
FROM tbl_payment_master AS a
INNER JOIN tbl_user_registration AS b
ON a.user_id = b.user_id
WHERE a.premium_amount != 0
AND a.user_id = #user_id
My table structure
create table tbl_userregistration(user_id bigint primary key identity,name nvarchar(100))
create table tbl_payment_master(payment_id bigint primary key identity,paid_date datetime,amount float,user_id bigint foreign key references tbl_user_registration(user_id)) )
How could be improve the performance.
Any alter solution to retrieve data's .
Not knowing anything else, I did a mechanical refactoring of the correlated subqueries into joins. This MAY or MAY NOT be helpful depending on your setup.
The reason this may work is because with a join, the dataset is calculated generally once, but for a correlated subquery, it is running the sub query once for each record in the return.
This technique demonstrates something called a derived table.
Also, the userid filter in the derived table may or may not be needed or useful.
SELECT a.user_id
,b.NAME
,a.paid_date
,paymentDateCount = noofpaymnt.userCount
,PaymentMonthCount = noofpaymonth.userCount
FROM tbl_payment_master AS a
INNER JOIN tbl_user_registration AS b ON a.user_id = b.user_id
INNER JOIN (
SELECT user_id
,paid_date
,userCount = Count(user_id)
FROM tbl_payment_master
WHERE user_id = #user_id AND a.premium_amount != 0
GROUP BY user_id
,paid_date
) noofpaymnt ON a.user_id = noofpaymnt.user_id
AND a.paid_date = noofpaymnt.paiddate
INNER JOIN (
SELECT user_id
,paymentmonth = month(paid_date)
,paymentyear = year(paiddate)
,userCount = Count(user_id)
FROM tbl_payment_master
WHERE user_id = #user_id AND a.premium_amount != 0
GROUP BY user_id
,month(paid_date)
,year(paid_date)
) noofpaymonth ON a.user_id = noofpaymonth.user_id
AND month(a.paid_date) = noofpaymonth.paymentmonth
AND year(a.paid_date) = noofpaymonth.paymentyear
WHERE a.premium_amount != 0
AND a.user_id = #user_id
I'm very sure there's a better way to write this, but I think you want something in the lines of this:
select
a.user_id
, b.name
, aggr.usr_cnt
, aggr2.usr_cnt
from tbl_payment_master as a
inner join tbl_user_registration as b
on a.user_id = b.user_id
left join (select
count(aa.user_id) usr_cnt
, aa.user_id
, aa.paid_date
from tbl_payment_master aa
where aa.paid_date = a.paid_date
and aa.user_id = #user_id
group by aa.paid_date, aa.user_id
) aggr on aggr.user_id = a.user_id
and aggr.paid_date = a.paid_date
left join (select
count(aa.user_id) usr_cnt
, aa.user_id
, month(aa.paid_date) paid_date_month
, year(aa.paid_date) paid_date_year
from tbl_payment_master aa
where month(aa.paid_date) = month(a.paid_date)
and year(aa.paid_date) = year(a.paid_date)
and aa.user_id = #user_id
group by aa.user_id
, month(aa.paid_date)
, year(aa.paid_date)
) aggr2 on aggr2.user_id = a.user_id
and aggr2.paid_date_month = month(a.paid_date)
and aggr2.paid_date_year = year(a.paid_date)
where a.premium_amount <> 0
and a.user_id = #user_id;
PS: It would be much easier to test and improve this query on real data
Index all fields that are read by the database.
This should speed up performance.
I get a list of values from the select query. From the list, I am checking whether the items in the list is available in the table. If the values exist, I need to update the values else Insert the list into the Table.
With the list, I can insert the list of values in the table.
How to check and update the list in sql.
My Query :
WITH pq AS
(
SELECT A.[ProductId] ,A.[Quantity],A.[OrderId],D.[ProductName],E.[SpecialPrice],E.[SpecialPrice]*A.[Quantity] AS SPrice FROM [Table1] A
LEFT JOIN [Table2] B ON A.[OrderId] = B.[OrderId] INNER JOIN [Table3] D
ON A.[ProductId] = D.[ProductId] INNER JOIN [Table4] E
ON A.[ProductId] = E.[ProductId] WHERE B.[CustomerId] = 1
AND A.[OrderId] = 77
)
IF (EXISTS(SELECT [ProductId] FROM [Table5] WHERE [ProductId] = A.[ProductId]))
BEGIN
UPDATE [Table5]
SET [Quantity] = A.[Quantity]
WHERE B.[CustomerId] = 1 AND [ProductId] = A.[ProductId]
END
ELSE
BEGIN
INSERT INTO [Table5]
([ProductId],[ProductName],[Quantity],[Price],[TotalAmount])
SELECT
[ProductId],[ProductName],[Quantity],[SpecialPrice],SPrice
FROM pq;
END
Any suggestions will be greatly helpful.
EDIT : SELECT QUERY RESULT
ProductId Quantity
65 2
64 1
Assuming you're on SQL Server 2008 or above, the MERGE statement will solve your problem:
MERGE Table5 TRG
USING (
SELECT
A.ProductId,
A.Quantity,
A.OrderId,
D.ProductName,
E.SpecialPrice,
(E.SpecialPrice * A.Quantity) SPrice
FROM Table1 A
LEFT JOIN Table2 B ON A.OrderId = B.OrderId
INNER JOIN Table3 D ON A.ProductId = D.ProductId
INNER JOIN Table4 E ON A.ProductId = E.ProductId
WHERE
B.CustomerId = 1
AND A.OrderId = 77
) SRC
ON TRG.ProductID = SRC.ProductID
WHEN MATCHED THEN
UPDATE SET TRG.Quantity = SRC.Quantity
WHEN NOT MATCHED BY TARGET THEN
INSERT (
ProductId
, ProductName
, Quantity
, Price
, TotalAmount
)
VALUES (
SRC.ProductId
, SRC.ProductName
, SRC.Quantity
, SRC.SpecialPrice
, SRC.SPrice)
;
You can move the SELECT query out to a CTE for legibility like you did in your example.