Best way to join this into a temp table and inner join - sql

Just wondering the best way to put this into a temp table and then join it.
IF EXISTS(SELECT LocId
FROM dbo.Locations WITH (NOLOCK)
WHERE SourceSystem = #SourceSystem
AND LocId IN (SELECT ListVal
FROM etopsuser.fnParseListToTable(#LocIdList, ';')) AND IsHot = 1)
BEGIN
Specifically trying to do it on this line of code
(SELECT ListVal
FROM etopsuser.fnParseListToTable(#LocIdList, ';')) AND IsHot = 1)
The NOLOCK is unrelated

You would create a temporary table just like any other table from a select:
SELECT ListVal
INTO #templist
FROM etopsuser.fnParseListToTable(#LocIdList, ';');
Then you would use it as:
SELECT l.LocId
FROM dbo.Locations l JOIN
#templist tl
ON l.LocId = tl.Listval
WHERE l.SourceSystem = #SourceSystem AND l.IsHot = 1

The best way to pass a list into a procedure is to use a Table Valued Parameter
CREATE TYPE dbo.List AS TABLE (ListVal varchar(255));
IF EXISTS(SELECT 1
FROM dbo.Locations l
WHERE l.SourceSystem = #SourceSystem
AND l.LocId IN (
SELECT ll.ListVal
FROM #LocIdList ll
) AND IsHot = 1)
Notes: Always use table references on every column, especially if subqueries are involved. Never use NOLOCK unless you are prepared for incorrect results. EXISTS ignores its SELECT, so SELECT 1 or SELECT NULL is standard.
Then you can pass in the table variable either from client code depending on language, or in T-SQL like this
DECLARE #list dbo.List;
INSERT #list (ListVal)
VALUES ('SomeValue');
EXEC YourProc #LocIdList = #list;

Related

Replacing Is Null Or Exist Inner Query Logic to Reduce Stored Procedure Execution Time

My stored procedure is currently using Is Null Or Exist logic combined with an inner query to filter out the records. The stored procedure is converting multiple comma-separated input values to temp tables (in the production scenario, the input record count will be much higher). And the inner query is using these temp tables for filter conditions. Due to the concern over query execution time would like to change the existing inner-query with an alternate (like left join). But need to retain the same Is Null Or Exist logic. Any suggestions?
DECLARE #SelectedOfferes varchar(1000) = 'FLT10,SPL20'
DECLARE #SelectedBrandCode varchar(1000) = '208,406'
DECLARE #CategoryCode varchar(1000) = 'GMOVN2,CELSMR,LCDTV38IN'
CREATE TABLE #SelectedOfferes
(
DiscountCode VARCHAR(20)
)
CREATE TABLE #BrandCode
(
BrandCode VARCHAR(20)
)
CREATE TABLE #CategoryCode
(
CategoryCode VARCHAR(20)
)
IF #SelectedOfferes IS NOT NULL
BEGIN
INSERT INTO #SelectedOfferes
SELECT part
FROM dbo.[FormatTextByDelimiter] (#SelectedOfferes, ',')
END
IF #SelectedBrandCode IS NOT NULL
BEGIN
INSERT INTO #BrandCode
SELECT part
FROM dbo.[FormatTextByDelimiter] (#SelectedBrandCode, ',')
END
IF #CategoryCode IS NOT NULL
BEGIN
INSERT INTO #CategoryCode
SELECT part
FROM dbo.[FormatTextByDelimiter] (#CategoryCode, ',')
END
SELECT *
FROM Products P
INNER JOIN Discount D ON P.DiscountCode = D.DiscountCode
INNER JOIN AvailableBrand AB ON P.BrandCode = AB.BrandCode
INNER JOIN Category C ON P.CategoryCode = C.CategoryCode
WHERE (#SelectedOfferes IS NULL
OR (EXISTS (SELECT 1 FROM #SelectedOfferes OFR
WHERE OFR.DiscountCode = P.DiscountCode)))
AND (#SelectedBrandCode IS NULL
OR (EXISTS (SELECT 1 FROM #BrandCode BC
WHERE BC.BrandCode = P.BrandCode)))
AND (#CategoryCode IS NULL
OR (EXISTS (SELECT 1 FROM #CategoryCode CAT
WHERE CAT.CategoryCode = P.CategoryCode)))
Dynamic SQL version
I have some questions about your string split function, is it set-based or a looping query? If it's not set-based then you should probably replace it with Jeff Moden's DelimitedSplit8K available at http://www.sqlservercentral.com/articles/Tally+Table/72993/ .
The below example should work the same as what you supplied but should be faster since it removes the ORs and the correlated subqueries from the WHERE clause. I'm not a fan of using dynamic SQL but sometimes it is the best way to get the job done. Maybe someone else can come up with a non-dynamic solution that works as well or better.
DECLARE #SelectedOfferes varchar(1000) = 'FLT10,SPL20'
DECLARE #SelectedBrandCode varchar(1000) = '208,406'
DECLARE #CategoryCode varchar(1000) = 'GMOVN2,CELSMR,LCDTV38IN'
CREATE TABLE #SelectedOfferes
(
DiscountCode VARCHAR(20)
)
CREATE TABLE #BrandCode
(
BrandCode VARCHAR(20)
)
CREATE TABLE #CategoryCode
(
CategoryCode VARCHAR(20)
)
IF #SelectedOfferes IS NOT NULL
BEGIN
INSERT INTO #SelectedOfferes
SELECT part
FROM dbo.[FormatTextByDelimiter] (#SelectedOfferes, ',')
END
IF #SelectedBrandCode IS NOT NULL
BEGIN
INSERT INTO #BrandCode
SELECT part
FROM dbo.[FormatTextByDelimiter] (#SelectedBrandCode, ',')
END
IF #CategoryCode IS NOT NULL
BEGIN
INSERT INTO #CategoryCode
SELECT part
FROM dbo.[FormatTextByDelimiter] (#CategoryCode, ',')
END
DECLARE #SQL NVarchar(4000);
SET #SQL = N'SELECT *
FROM Products P
INNER JOIN Discount D ON P.DiscountCode = D.DiscountCode
INNER JOIN AvailableBrand AB ON P.BrandCode = AB.BrandCode
INNER JOIN Category C ON P.CategoryCode = C.CategoryCode'
IF #SelectedOfferes IS NOT NULL
SET #SQL = #SQL + N'
INNER JOIN #SelectedOfferes OFR ON OFR.DiscountCode = P.DiscountCode';
IF #SelectedBrandCode IS NOT NULL
SET #SQL = #SQL + N'
INNER JOIN #BrandCode BC ON BC.BrandCode = P.BrandCode';
IF #CategoryCode IS NOT NULL
SET #SQL = #SQL + N'
INNER JOIN #CategoryCode CAT ON CAT.CategoryCode = P.CategoryCode';
EXEC sys.sp_executesql #stmt = #SQL;
This method doesn't quite do what the OP wanted but is valid in many other cases
I have some questions about your string split function, is it set-based or a looping query? If it's not set-based then you should probably replace it with Jeff Moden's DelimitedSplit8K available at http://www.sqlservercentral.com/articles/Tally+Table/72993/ .
But either way the below change to your last query should help quite a bit. The IS NULL parts aren't needed since it is a LEFT JOIN and the table will be empty if the variable it is built with is NULL, so you get the same result with less work for the engine.
SELECT *
FROM Products P
INNER JOIN Discount D ON P.DiscountCode = D.DiscountCode
INNER JOIN AvailableBrand AB ON P.BrandCode = AB.BrandCode
INNER JOIN Category C ON P.CategoryCode = C.CategoryCode
LEFT JOIN #SelectedOfferes OFR ON OFR.DiscountCode = P.DiscountCode
LEFT JOIN #BrandCode BC ON BC.BrandCode = P.BrandCode
LEFT JOIN #CategoryCode CAT ON CAT.CategoryCode = P.CategoryCode

SQL query with "not in" is very slow

I have the following SQL query which is very slow. How can I write the script differently?
select
pws_name
from
pws_asset ass
join
Account acc on acc.AccountId = ass.pws_AccountId
where
acc.AccountNumber in ('188012', '172146', '214727', '13636', '201194', '280294', '34328')
and ass.pws_name not in ('1018684', '1018784', '1019584', '1019784', '1019884', '1070838', '1277139', '1277339'.........)
Kindly follow below steps, that will help in query performance.
Step 1: DECLARE two variables
DECLARE #AccNumList VARCHAR(4000)
DECLARE #PwsNameList VARCHAR(4000)
SET #AccNumList = '188012,172146,214727,13636,201194,280294,34328'
SET #PwsNameList = '1018684,1018784,1019584,1019784,1019884,1070838,1277139,1277339'
Step 2: Create two different temporary tables.
1 for account numbers
Create table #tblAcNum(AccountNumber VARCHAR(50))
2 for pws_name not needed
Create table #tblPwsNameNotNeeded(pws_name VARCHAR(50))
Step 3: Add records in above two tables which are used in IN and NOT IN.
Please check this Split csv string using XML in SQL Server for reference.
INSERT INTO #tblAcNum(AccountNumber)
SELECT
l.value('.','VARCHAR(50)') AcNum
FROM
(
SELECT CAST('<a>' + REPLACE(#AccNumList,',','</a><a>') + '</a>' AS XML) AcNumXML
) x
CROSS APPLY x.AcNumXML.nodes('a') Split(l)
INSERT INTO #tblPwsNameNotNeeded(pws_name)
SELECT
l.value('.','VARCHAR(50)') pws_name
FROM
(
SELECT CAST('<a>' + REPLACE(#PwsNameList,',','</a><a>') + '</a>' AS XML) PwsNameXML
) x
CROSS APPLY x.PwsNameXML.nodes('a') Split(l)
Step 3: INNER JOIN #tblAcNum table with account table with accountnumber column
Step 4: Use NOT EXISTS() function for pws_name not needed like below
WHERE NOT EXISTS
(
SELECT 1
FROM #tblPwsNameNotNeeded pn
Where pn.pws_name = ass.pws_name
)
Step 5: Drop temporary tables after your select query.
DROP TABLE tblAcNum;
DROP TABLE #tblPwsNameNotNeeded;
Please check below query.
DECLARE #AccNumList VARCHAR(4000)
DECLARE #PwsNameList VARCHAR(4000)
SET #AccNumList = '188012,172146,214727,13636,201194,280294,34328'
SET #PwsNameList = '1018684,1018784,1019584,1019784,1019884,1070838,1277139,1277339'
Create table #tblAcNum(AccountNumber VARCHAR(50))
Create table #tblPwsNameNotNeeded(pws_name VARCHAR(50))
INSERT INTO #tblAcNum(AccountNumber)
SELECT
l.value('.','VARCHAR(50)') AcNum
FROM
(
SELECT CAST('<a>' + REPLACE(#AccNumList,',','</a><a>') + '</a>' AS XML) AcNumXML
) x
CROSS APPLY x.AcNumXML.nodes('a') Split(l)
INSERT INTO #tblPwsNameNotNeeded(pws_name)
SELECT
l.value('.','VARCHAR(50)') pws_name
FROM
(
SELECT CAST('<a>' + REPLACE(#PwsNameList,',','</a><a>') + '</a>' AS XML) PwsNameXML
) x
CROSS APPLY x.PwsNameXML.nodes('a') Split(l)
select
ass.pws_name
from pws_asset ass
join Account acc on acc.AccountId = ass.pws_AccountId
INNER JOIN #tblAcNum an ON an.AccountNumber = acc.AccountNumber
WHERE NOT EXISTS
(
SELECT 1
FROM #tblPwsNameNotNeeded pn
Where pn.pws_name = ass.pws_name
)
DROP TABLE tblAcNum;
DROP TABLE #tblPwsNameNotNeeded;
Try this:
; with cte_excludepws as
(select AccountId from pws_asset where pws_name not in ('1018684', '1018784', '1019584', '1019784', '1019884', '1070838', '1277139', '1277339'.........))
select
pws_name
from
pws_asset ass
join
Account acc on acc.AccountId = ass.pws_AccountId
where ass.AccountId not in (select AccountId from cte_excludepws)
and acc.AccountNumber in ('188012', '172146', '214727', '13636', '201194', '280294', '34328')
Alternatively if you can - take the AccountID's into a temporary table instead of cte and create an index on it.
First, be sure that the account numbers are really strings. If they are numbers, drop the single quotes!
Then, for this query
select a.pws_name
from pws_asset a join
Account ac
on ac.AccountId = a.pws_AccountId
where ac.AccountNumber in ('188012', '172146', '214727', '13636', '201194', '280294', '34328') and
a.pws_name not in ('1018684', '1018784', '1019584', '1019784', '1019884', '1070838', '1277139', '1277339'.........);
I would recommend indexes on: account(accountNumber, AccountId) and pws_asset(pws_AccountId, pws_name).

Inner Join with order by and where clase

I have created a stored procedure for filling a drop down. But the order by clause not working on my procedure.
ALTER PROCEDURE proc
-- Add the parameters for the stored procedure here
#compID bigint,
#after datetime
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
CREATE TABLE #tmpAuc ( ProjectID BIGINT, Title VARCHAR(256))
INSERT INTO #tmpAuc
SELECT SA.ID ProjectID, SA.Title
FROM [dbo].[Sessions] S
INNER JOIN Auc SA ON S.AucID = SA.ID
WHERE S.Session < 3 AND SA.Status > 0 AND SA.CompanyID = #companyID AND S.LiveBeginDate > #after
ORDER BY LiveBeginDate
SELECT DISTINCT * FROM #tmpAuc
END
I would like to order by descending order of LiveBehinDate
Include the LiveBeginDate in the temp table and from the temp table result ORDER BY LiveBeginDate
CREATE TABLE #tmpAuctions (ProjectID BIGINT, Title VARCHAR(256), LiveBeginDate DATETIME)
INSERT INTO #tmpAuctions (ProjectID, Title, LiveBeginDate)
SELECT SA.ID AS ProjectID, SA.Title, S.LiveBeginDate
FROM [dbo].[Sessions] S
INNER JOIN [Spectrum_Auctions].[dbo].[Auctions] SA ON S.AuctionID = SA.ID
WHERE S.SessionState < 3 AND SA.Status > 0 AND SA.CompanyID = #companyID AND S.LiveBeginDate > #after
SELECT DISTINCT *
FROM #tmpAuctions
ORDER BY LiveBeginDate
or avoid temp table and directly use the SELECT with JOIN inside the procedure:
SELECT SA.ID AS ProjectID, SA.Title
FROM [dbo].[Sessions] S
INNER JOIN [Spectrum_Auctions].[dbo].[Auctions] SA ON S.AuctionID = SA.ID
WHERE S.SessionState < 3 AND SA.Status > 0 AND SA.CompanyID = #companyID AND S.LiveBeginDate > #after
ORDER BY S.LiveBeginDate
That’s because your data is ordered when writing to the temp table that resides in temp dB. Reading from temp dB is never guaranteed to be in order. So when you select star from your temp table that’s what you get.
Get rid of the temp table and do the select directly. This will be faster and more efficient as well. If your proc becomes more complex, use CTEs instead of temp tables as they are easier to conceptualize and run much faster in all cases.

A nested INSERT, UPDATE, DELETE, or MERGE statement must have an OUTPUT clause in UPDATE

I'm trying to update some values based on every Id in the list. The logic I have seems to be what I want.
I want to populate a temporary table of Ids. Then for every ID I want to apply this query and output the deleted date and the ID into a new table I've created.
I keep getting the error:
Msg 10716, Level 15, State 1, Line 25
A nested INSERT, UPDATE, DELETE, or MERGE statement must have an OUTPUT clause.
What does this mean? I thought I am OUTPUTTING into the new table I've created.
USE datatemp
GO
DECLARE #idlist TABLE (id INT)
INSERT INTO #idlist (id) VALUES (3009099)
DECLARE #EndDate DATETIME
SET #EndDate = '2099-12-12'
IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'TEMP_TABLE')
BEGIN
CREATE TABLE [TEMP_TABLE] (
[id] INT,
[thedatetoend] DATETIME);
END
BEGIN TRY
SELECT *
FROM #idlist AS idlist
OUTER APPLY(
UPDATE [custprofile]
SET thedatetoend = #EndDate
OUTPUT idlist.id, DELETED.thedatetoend
INTO [TEMP_TABLE]
FROM [custprofile] as bc
INNER JOIN [custinformation] as cc
ON cc.custengageid = bc.custengageid
WHERE cc.id = idlist.id
AND bc.modifierid = 2
AND bc.thedatetoend > GETDATE()
AND cc.type = 1) o
I think you may have more success by using a CTE and avoiding the outer apply approach you are currently using. Updates made to the CTE cascade to the source table. It might look something like the following but as some columns don't reference the table aliases don't expect this to work "as is" (i.e. I'm not sure if you are outputting ccid or bcid and I don't know which table thedatetoend belongs to.)
WITH
CTE AS (
SELECT
cc.id AS ccid, bcid AS bcid, thedatetoend
FROM [custprofile] AS bc
INNER JOIN [custinformation] AS cc ON cc.custengageid = bc.custengageid
INNER JOIN #idlist AS idlist ON cc.id = idlist.id
WHERE bc.modifierid = 2
AND bc.thedatetoend > GETDATE()
AND cc.type = 1
)
UPDATE CTE
SET thedatetoend = #EndDate
OUTPUT ccid, DELETED.thedatetoend
INTO [TEMP_TABLE]

SQL - Joining tables where one of the columns is a list

I'm tryin to join two tables. The problem i'm having is that one of the columns i'm trying to join on is a list.
So is it possible to join two tables using "IN" rather than "=". Along the lines of
SELECT ID
FROM tableA INNER JOIN
tableB ON tableB.misc IN tableA.misc
WHERE tableB.miscTitle = 'help me please'
tableB.misc = 1
tableA.misc = 1,2,3
Thanks in advance
No what you want is not possible without a major workaround. DO NOT STORE ITEMS YOU WANT TO JOIN TO IN A LIST! In fact a comma delimited list should almost never be stored in a database. It is only acceptable if this is note type information that will never need to be used in a query where clasue or join.
If you are stuck with this horrible design, then you will have to parse out the list to a temp table or table variable and then join through that.
Try this:
SELECT ID
FROM tableA INNER JOIN
tableB ON ',' + TableA.misc + ',' like '%,' + cast(tableB.misc as varchar) + ',%'
WHERE tableB.miscTitle = 'help me please'
A string parsing function like the one found here together with a CROSS APPLY should do the trick.
CREATE FUNCTION [dbo].[fnParseStringTSQL] (#string NVARCHAR(MAX),#separator NCHAR(1))
RETURNS #parsedString TABLE (string NVARCHAR(MAX))
AS
BEGIN
DECLARE #position int
SET #position = 1
SET #string = #string + #separator
WHILE charindex(#separator,#string,#position) <> 0
BEGIN
INSERT into #parsedString
SELECT substring(#string, #position, charindex(#separator,#string,#position) - #position)
SET #position = charindex(#separator,#string,#position) + 1
END
RETURN
END
go
declare #tableA table (
id int,
misc char(1)
)
declare #tableB table (
misc varchar(10),
miscTitle varchar(20)
)
insert into #tableA
(id, misc)
values
(1, '1')
insert into #tableB
(misc, miscTitle)
values
('1,2,3','help me please')
select id
from #tableB b
cross apply dbo.fnParseStringTSQL(b.misc,',') p
inner join #tableA a
on a.misc = p.string
where b.miscTitle = 'help me please'
drop function dbo.fnParseStringTSQL
Is ID also in tableB? If so, you can reverse the tables, and run the IN backwards, in the WHERE section, like so:
SELECT ID
FROM tableB
WHERE tableB.miscTitle = 'help me please'
AND tableB.misc IN (SELECT tableA.misc FROM tableA)
If it's not, you could use a cross join to get all combinations of rows between the tables, then remove the rows that don't obey the IN. WARNING: This will become a huge join if the tables are large. Example:
SELECT ID
FROM tableA
CROSS JOIN tableB
WHERE tableB.miscTitle = 'help me please'
AND tableB.misc IN tableA.misc
EDIT: didn't realize "in a list" meant a comma-delimited VARCHAR. SQL's IN won't work for that, nor should you ever store joinable data that way in a database.