sql server 2008 stored procedure optimization - sql-server

I have a stored procedure, its really simple, just one select statement, however the where clause changes based on the parameters.
I am using if else statement and wrote out the select 4 times, is there a better way to simplify this stored procedure?
IF #Status > 0
BEGIN
IF #Group = ''
BEGIN
SELECT *
FROM ( SELECT tbl.* ,
ROW_NUMBER() OVER ( ORDER BY tbl.NDB_No ) rownum
FROM dbo.FoodAbbrev AS tbl
WHERE Status = #Status
) seq
WHERE seq.rownum BETWEEN #X AND #Y
ORDER BY seq.rownum
END
ELSE
BEGIN
SELECT *
FROM ( SELECT tbl.* ,
ROW_NUMBER() OVER ( ORDER BY tbl.NDB_No ) rownum
FROM dbo.FoodAbbrev AS tbl
WHERE Status = #Status
AND GroupCd = #Group
) seq
WHERE seq.rownum BETWEEN #X AND #Y
ORDER BY seq.rownum
END
END
ELSE
BEGIN
IF #Group = ''
BEGIN
SELECT *
FROM ( SELECT tbl.* ,
ROW_NUMBER() OVER ( ORDER BY tbl.NDB_No ) rownum
FROM dbo.FoodAbbrev AS tbl
) seq
WHERE seq.rownum BETWEEN #X AND #Y
ORDER BY seq.rownum
END
ELSE
BEGIN
SELECT *
FROM ( SELECT tbl.* ,
ROW_NUMBER() OVER ( ORDER BY tbl.NDB_No ) rownum
FROM dbo.FoodAbbrev AS tbl
WHERE GroupCd = #Group
) seq
WHERE seq.rownum BETWEEN #X AND #Y
ORDER BY seq.rownum
END
END
END

Replace all these IF statements with the following one SELECT statments with optional parameters:
SELECT *
FROM ( SELECT tbl.* ,
ROW_NUMBER() OVER ( ORDER BY tbl.NDB_No ) rownum
FROM dbo.FoodAbbrev AS tbl
WHERE 1 = 1
AND (#Group IS NULL OR GroupCd = #Group)
AND (#Status IS NULL OR Status = #Status)
) seq
WHERE seq.rownum BETWEEN #X AND #Y
ORDER BY seq.rownum

SELECT *
FROM ( SELECT tbl.* ,
ROW_NUMBER() OVER ( ORDER BY tbl.NDB_No ) rownum
FROM dbo.FoodAbbrev AS tbl
WHERE ISNULL(Status,0) = CASE WHEN #Status > 0 THEN #Status ELSE ISNULL(Status,0) END
AND ISNULL(GroupCd, '') = CASE WHEN #Group = '' THEN ISNULL(GroupCd, '') ELSE #Group END
) seq
WHERE seq.rownum BETWEEN #X AND #Y
ORDER BY seq.rownum

Related

How to get all the single number from [number -(to) number] with comma-seperated in a column in sql server

column of data in sqlserver like
numbers
1000-1050, 1054, 1090-1230, 1245
numbers
-------
1000-1050, 1054, 1090-1230, 1245
how to get the style like below:
numbers
-------
1000
1001
1002
1003
1004
...
1050
1054
1090
1091
1092
...
1245
You could use a split function and APPLY like this
DECLARE #SampleData AS TABLE
(
numbers varchar(200)
)
INSERT INTO #SampleData
VALUES ('1000-1050, 1054, 1090-1230, 1245')
;WITH temp AS
(
SELECT 1 AS Number
UNION ALL
SELECT t.Number + 1 FROM temp t
WHERE t.Number < 5000
) -- return table from 1 --> 5000
SELECT DISTINCT
ca2.*
FROM #SampleData sd
CROSS APPLY
(
SELECT pos, LTRIM(Value) AS Value
FROM dbo.SplitString(sd.Numbers,',')
) ca1
CROSS APPLY
(
SELECT *
FROM temp t WHERE t.Number BETWEEN LEFT(ca1.[Value], charindex('-', ca1.[Value] + '-') - 1) AND
CASE
WHEN len(ca1.[Value]) < charindex('-', ca1.[Value] + '-') THEN ca1.[Value]
ELSE RIGHT(ca1.[Value], len(ca1.[Value]) - charindex('-', ca1.[Value] + '-'))
END
) ca2
OPTION (MAXRECURSION 0)
Split function
CREATE FUNCTION [dbo].[SplitString] (#Text varchar(max),#Delimiter varchar(10))
Returns Table
As
Return (
Select Pos = Row_Number() over (Order By (Select null))
,Value = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>'+ Replace(#Text,#Delimiter,'</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
);
Demo link: http://rextester.com/GIPGR78132
First split the comma seperated values, then get the values using a recursive common table expression.
Declare #values nvarchar(max) = '1000-1050, 1054, 1090-1230, 1245'
;with ranges_cte as
(
select cast(case pos when 0 then ResultValue else left(ResultValue,pos-1) end as int) first, cast(case pos when 0 then ResultValue else substring(ResultValue,pos+1,len(ResultValue)-pos) end as int) Last
from (
select ResultValue, charINDEx('-',ResultValue) pos
from dbo.SplitString(#values,',')) x
)
, values_rte as
(
select first,last,first as active
from ranges_cte
union all
select first,last,active +1 as active
from values_rte
where active< last
)
select *
from values_rte
OPTION (MAXRECURSION 0)
Function to split the values:
Create FUNCTION [dbo].[SplitString] (#StringArray NVARCHAR(MAX), #Delimiter NVARCHAR(10))
RETURNS #ResultedValues table
(
nr int,
ResultValue nvarchar(max)
)
AS
--select * from dbo.splitstring ('123,456,789',',')
BEGIN
declare #string nvarchar(max)
declare #nr int = 1
set #string = #StringArray
WHILE (CHARINDEX(#Delimiter,#String)>0)
BEGIN
INSERT INTO #ResultedValues (nr,ResultValue) VALUES (#nr,LTRIM(RTRIM(SUBSTRING(#String,1,CHARINDEX(#Delimiter,#String)-1))))
SET #String = SUBSTRING(#String, CHARINDEX(#Delimiter,#String)+LEN(#Delimiter),LEN(#String))
set #nr = #nr +1
END
INSERT INTO #ResultedValues (nr,ResultValue ) VALUES ( #nr, LTRIM(RTRIM(#String)))
RETURN
END
Create a split function:
CREATE FUNCTION [dbo].[SplitIDsTest]
(
#idList varchar(4000)
)
RETURNS #parsedList TABLE
(
Id varchar(50),
Nr int
)
AS
BEGIN
DECLARE #id varchar(10), #pos int
DECLARE #nr int = 0;
SET #idList = LTRIM(RTRIM(#idList)) + ','
SET #Pos = CHARINDEX(',', #idList)
IF REPLACE(#idList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #id = LTRIM(RTRIM(LEFT(#idList, #pos - 1)))
IF #id <> ''
BEGIN
set #nr += 1;
INSERT INTO #ParsedList (Id, Nr)
VALUES (#id, #nr);
END
SET #idList = RIGHT(#idList, LEN(#idList) - #pos) -- 'WMPC,' (inklusive Komma) links vom Eingabeparameter abschneiden, weil jetzt der nächste Wert gesucht wird
SET #pos = CHARINDEX(',', #idList, 1) -- Nächste Position eines Kommas suchen und in der WHILE-Schleife weitermachen
END
END
RETURN
END
Then a tally table:
select top 1000000 N=identity(int, 1, 1)
into dbo.Tally
from master.dbo.syscolumns a cross join master.dbo.syscolumns b;
Then use:
select distinct Tally.N
from SplitIDsTest('10-12, 34, 9') splitted
join Tally on 1 = case
when CHARINDEX('-', splitted.Id) > 0 then
case
when Tally.N between cast(left(splitted.Id, CHARINDEX('-', splitted.Id) - 1) as int)
and cast(right(splitted.Id, len(splitted.Id) - CHARINDEX('-', splitted.Id)) as int) then 1
else 0
end
when Tally.N = cast(splitted.Id as int) then 1
else 0
end
order by Tally.N

SQL Server 2014 Loop untill the next row is not null

I am trying to select the second row in a column, I want to continue the loop if the next row is null. Here is my script:
DECLARE #MAXID INT, #Counter INT, #clientId AS int
SET #COUNTER = 2
SET #clientId = 11
SELECT #MAXID = COUNT(DISTINCT vw_masterView.LastVisitDate) FROM vw_MasterView where clientId = #clientId;
WHILE (#COUNTER <= #MAXID)
BEGIN
SELECT myData FROM
(
SELECT myData , ROW_NUMBER() OVER (order by vw_masterView.LastDate desc) AS Rownumber
FROM vw_MasterView where clientId = #clientId
) results
WHERE results.Rownumber = #COUNTER
IF Results.myData IS NOT NULL BREAK;
SET #COUNTER = #COUNTER + 1
END
I get the following error:
The multi-part identifier "Results.myData" could not be bound.
Thank you
There is no variable called Results in there. Set myData at variable and check null with the variable. Try this:
BEGIN
DECLARE #myData VARCHAR(20)
SELECT #myData = myData FROM
(
SELECT
myData,
ROW_NUMBER() OVER (order by vw_masterView.LastDate desc) AS Rownumber
FROM vw_MasterView where clientId = #clientId
) results
WHERE results.Rownumber = #COUNTER
IF #myData IS NOT NULL BREAK;
SET #COUNTER = #COUNTER + 1
END
Your use of the Results.myData is deeply flawed. You are not inside the query itself, so you can't reference a table and column. I believe this will accomplish what you are trying to do though.
DECLARE #clientId AS int = 11
SELECT
*
FROM (
SELECT myData , ROW_NUMBER() OVER (order by vw_masterView.LastDate desc) AS Rownumber
FROM vw_MasterView
where clientId = #clientId
and myData is not null
) a
WHERE Rownumber=2

how shall i transpose rows into new columns only if duplicates appears in rows?

I have a table like this:
I need to get output like this:
I tried to use transposing, but I am not getting someone please give idea to do this.
Creation script for table:
CREATE TABLE #T1 (
itemid int,
pack int,
UOM nvarchar(2)
)
INSERT INTO #T1 VALUES
(1,1,'EA'),
(1,10,'BX'),
(1,100,'CA'),
(2,1,'EA'),
(2,10,'RL')
If you need simple, fixed column solution, you can use:
SELECT * INTO #temp FROM
(VALUES
(1, 1, 'EA'),
(1, 10, 'BX'),
(1, 100, 'CA'),
(2, 1, 'EA'),
(2, 10, 'RL')) T(ItemId, Pack, UOM)
SELECT ItemId, MAX([1]) Pack1, MAX([U1]) UOM1, MAX([2]) Pack1, MAX([U2]) UOM1, MAX([3]) Pack1, MAX([U3]) UOM1 FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack) PackNum,
'U'+CONVERT(varchar(10), ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack)) UOMNum
FROM #temp
) T
PIVOT
(
MAX(Pack) FOR PackNum IN ([1], [2], [3])
) PPack
PIVOT
(
MAX(UOM) FOR UOMNum IN ([U1],[U2],[U3])
) PUOM
GROUP BY ItemId
If number of columns is not fixed, you must do it dynamically (see #gofr1) or:
DECLARE #count TABLE(N varchar(10))
INSERT #count
SELECT CONVERT(varchar(10), ROW_NUMBER() OVER (ORDER BY (SELECT 1)))
FROM #temp WHERE ItemId =
(
SELECT TOP 1 ItemId
FROM #temp
GROUP BY ItemId
ORDER BY COUNT(*) DESC
)
DECLARE #select varchar(MAX) = STUFF((SELECT ', MAX(['+N+']) Pack'+N+', MAX([U'+N+']) UOM'+N FROM #count FOR XML PATH('')), 1, 2, '')
DECLARE #pivot1 varchar(MAX) = STUFF((SELECT ', ['+N+']' FROM #count FOR XML PATH('')), 1, 2, '')
DECLARE #pivot2 varchar(MAX) = STUFF((SELECT ', [U'+N+']' FROM #count FOR XML PATH('')), 1, 2, '')
DECLARE #sql varchar(MAX) = '
SELECT ItemId, '+#select+' FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack) PackNum,
''U''+CONVERT(varchar(10), ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack)) UOMNum
FROM #temp
) T
PIVOT
(
MAX(Pack) FOR PackNum IN ('+#pivot1+')
) PPack
PIVOT
(
MAX(UOM) FOR UOMNum IN ('+#pivot2+')
) PUOM
GROUP BY ItemId'
EXEC(#sql)
Dynamic SQL and some pivoting:
DECLARE #sql nvarchar(max),
#columns nvarchar(max)
SELECT #columns = (
SELECT DISTINCT ','+QUOTENAME('pack' + cast(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY itemid) as nvarchar(max)))+','+
QUOTENAME('UOM'+ cast(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY itemid) as nvarchar(max)))
FROM #T1
FOR XML PATH('')
)
SELECT #sql = N'
SELECT *
FROM (
SELECT itemid,
Items+rn as Items,
[Values]
FROM (
SELECT itemid,
CAST(UOM as nvarchar(max)) as UOM,
CAST(pack as nvarchar(max)) as pack,
CAST(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY (SELECT NULL)) as nvarchar(max)) as rn
FROM #T1
) as t
UNPIVOT (
[Values] FOR Items in ([UOM],[pack])
) as up
) t1
PIVOT (
MAX([Values]) FOR Items IN ('+STUFF(#columns,1,1,'')+')
) as pvt'
EXEC sp_executesql #sql
Output:
itemid pack1 UOM1 pack2 UOM2 pack3 UOM3
1 1 EA 10 BX 100 CA
2 1 EA 10 RL NULL NULL
EDIT#1
There is one more way:
DECLARE #sql nvarchar(max),
#columns nvarchar(max),
#colsrn nvarchar(max)
;WITH cte AS (
SELECT DISTINCT cast(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY itemid) as nvarchar(max)) as rn
FROM #T1
)
SELECT #columns = (
SELECT ',MAX('+QUOTENAME('pack' + rn) +') as '+ QUOTENAME('pack' + rn) +',MAX('+
QUOTENAME('UOM'+ rn) +') as ' + QUOTENAME('UOM'+ rn)
FROM cte
FOR XML PATH('')
),
#colsrn = (
SELECT DISTINCT ',CASE WHEN rn = ' + rn +' THEN '+QUOTENAME('pack')+' ELSE NULL END as [pack'+rn+'],'
+'CASE WHEN rn = ' + rn +' THEN '+QUOTENAME('UOM')+' ELSE NULL END as [UOM'+rn+']'
FROM cte
FOR XML PATH('')
)
SELECT #sql = N'
SELECT itemid'+#columns+'
FROM (
SELECT itemid'+#colsrn+'
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY (SELECT NULL)) as rn
FROM #T1
) as t
) s
GROUP BY s.itemid'
EXEC sp_executesql #sql
Same output.
select itemid,
max(case when seq = 1 then pack end) pack1, max(case when seq = 1 then uom end)uom1,max(case when seq = 2 then pack end) pack2, max(case when seq = 2 then uom end) uom2,
max(case when seq = 3 then pack end) pack3, max(case when seq = 3 then uom end) uom3
from
(
select itemid, pack, uom, row_number() over(partition by itemid order by itemid) seq from T1
) d
group by itemid

Incorrect syntax near the keyword 'set' along with with clause

I have stored procedure logic as below. I get
Incorrect syntax near the keyword 'set'.
as error, please suggest me the solution.
Declare #VAL int
Declare #Region bigint
drop table Tempqq
Select * Into Tempqq From dbo.split(#SpokeFolderList, ',')
while (Select Count(*) From Tempqq) > 0
begin
Select Top 1 #VAL = Tempqq.val From Tempqq
;With MyCount AS
(
Select DispatchToRegionId ,FolderNo, row_number() OVER(ORDER BY FolderNo DESC) as Row
from tblTransite where FolderNo = #VAL group by DispatchToRegionId,FolderNo
)
set #Region =( Select top 1 DispatchToRegionId from MyCount
order by Row desc )
print #VAL
Delete Tempqq Where Tempqq.val = #VAL
end
Try This
Declare #VAL int
Declare #Region bigint
drop table Tempqq
Select * Into Tempqq From dbo.split(#SpokeFolderList, ',')
while (Select Count(*) From Tempqq) > 0
begin
Select Top 1 #VAL = Tempqq.val From Tempqq
;With MyCount AS
(
Select DispatchToRegionId ,FolderNo, row_number() OVER(ORDER BY FolderNo DESC) as Row
from tblTransite where FolderNo = #VAL group by DispatchToRegionId,FolderNo
)
Select top 1 DispatchToRegionId into #Region from MyCount
order by Row desc
print #VAL
Delete Tempqq Where Tempqq.val = #VAL
end
CTE is more like a VIEW rather than a stored procedure. It doesn't
seem to be correct syntax.
And Delete should have a From keyword
Try this
Declare #VAL int
Declare #Region bigint
Drop table Tempqq
Select * Into Tempqq From dbo.split(#SpokeFolderList, ',')
While (Select Count(*) From Tempqq) > 0
Begin
Select Top 1 #VAL = Tempqq.val From Tempqq
;With MyCount AS
(
Select DispatchToRegionId ,FolderNo, row_number() OVER(ORDER BY FolderNo DESC) as Row
From tblTransite where FolderNo = #VAL group by DispatchToRegionId,FolderNo
)
Select #Region =(Select top 1 DispatchToRegionId from MyCount order by Row desc)
Print #VAL
Delete From Tempqq Where Tempqq.val = #VAL
End

Find and Remove Repeated Substrings

I've a column in a SQL Server 2008 table where part of the string was accidentally repeated.
Does anyone have a quick and easy way to remove the trailing duplicated substring?
For example,
alpha\bravo\charlie\delta\charlie\delta
should be
alpha\bravo\charlie\delta
If you don't already have a numbers table:
SET NOCOUNT ON;
DECLARE #UpperLimit int = 4000;
;WITH n(rn) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY [object_id])
FROM sys.all_columns
)
SELECT [Number] = rn - 1
INTO dbo.Numbers FROM n
WHERE rn <= #UpperLimit + 1;
CREATE UNIQUE CLUSTERED INDEX n ON dbo.Numbers([Number]);
Now a generic split function, that will turn your delimited string into a set:
CREATE FUNCTION dbo.SplitString
(
#List nvarchar(max),
#Delim char(1)
)
RETURNS TABLE
AS
RETURN ( SELECT
rn,
vn = ROW_NUMBER() OVER (PARTITION BY [Value] ORDER BY rn),
[Value]
FROM
(
SELECT
rn = ROW_NUMBER() OVER (ORDER BY CHARINDEX(#Delim, #List + #Delim)),
[Value] = LTRIM(RTRIM(SUBSTRING(#List, [Number],
CHARINDEX(#Delim, #List + #Delim, [Number]) - [Number])))
FROM dbo.Numbers
WHERE Number <= LEN(#List)
AND SUBSTRING(#Delim + #List, [Number], 1) = #Delim
) AS x
);
GO
And then a function that puts them back together:
CREATE FUNCTION dbo.DedupeString
(
#List nvarchar(max)
)
RETURNS nvarchar(max)
AS
BEGIN
RETURN ( SELECT newval = STUFF((
SELECT N'\' + x.[Value] FROM dbo.SplitString(#List, '\') AS x
WHERE (x.vn = 1)
ORDER BY x.rn
FOR XML PATH(''), TYPE).value(N'./text()[1]', N'nvarchar(max)'), 1, 1, N'')
);
END
GO
Sample usage:
SELECT dbo.DedupeString(N'alpha\bravo\bravo\charlie\delta\bravo\charlie\delta');
Results:
alpha\bravo\charlie\delta
You can also say something like:
UPDATE dbo.MessedUpTable
SET OopsColumn = dbo.DedupeString(OopsColumn);
#MikaelEriksson will probably swoop in with a more efficient way to use XML to eliminate duplicates, but that is what I can offer until then. :-)
create function RemoveDups(#S nvarchar(max)) returns nvarchar(max)
as
begin
declare #R nvarchar(max)
declare #W nvarchar(max)
set #R = ''
while len(#S) > 1
begin
-- Get the first word
set #W = left(#S, charindex('/', #S+'/')-1)
-- Add word to result if not already added
if '/'+#R not like '%/'+#W+'/%'
begin
set #R = #R + #W + '/'
end
-- Remove first word
set #S = stuff(#S, 1, charindex('/', #S+'/'), '')
end
return left(#R, len(#R)- 1)
end
As requested by Aaron Bertrand. I will however make no claim on what is the fastest to execute.
-- Table to replace in
declare #T table
(
ID int identity,
Value nvarchar(max)
)
-- Add some sample data
insert into #T values ('alpha/beta/alpha/gamma/delta/gamma/delta/alpha')
insert into #T values ('delta/beta/alpha/beta/alpha/gamma/delta/gamma/delta/alpha')
-- Update the column
update T
set Value = NewValue
from (
select T1.ID,
Value,
stuff((select '/' + T4.Value
from (
select T3.X.value('.', 'nvarchar(max)') as Value,
row_number() over(order by T3.X) as rn
from T2.X.nodes('/x') as T3(X)
) as T4
group by T4.Value
order by min(T4.rn)
for xml path(''), type).value('.', 'nvarchar(max)'), 1, 1, '') as NewValue
from #T as T1
cross apply (select cast('<x>'+replace(T1.Value, '/', '</x><x>')+'</x>' as xml)) as T2(X)
) as T
select *
from #T

Resources