Looping SQL Server for one or more criteria - sql

I have SQL Server 2008 R2 and I am trying to run an query with a parameter from the client app that will return dynamic values. This should then be run independently for each parameter...
i.e.
PARAM VALUE contains 1,2,3,4,5
Loop for each <PARAM VALUE>
Select * from Table where ID = <PARAM VALUE>
Next
How would I loop for each comma delimited value and run the query independently?
The end all goal is I am going to insert each pass into a new temp table as results.

You can use a table variable to loop through the values:
DECLARE #Values TABLE (Index INT)
INSERT #Values VALUES (1, 2, 3, 4, 5)
DECLARE #Index INT
WHILE EXISTS (SELECT * FROM #Values) BEGIN
SET #Index = (SELECT TOP 1 Index FROM #Values)
SELECT * FROM Table WHERE ID = #Index
DELETE #Values WHERE Index = #Index
END
If the list of values is a sequence, then a more typical loop can be used:
DECLARE #Index INT = 1
WHILE #Index <= 5 BEGIN
SELECT * FROM Table WHERE ID = #Index
SET #Index = #Index + 1
END

Related

How to compare the values in a column to a long list in SQL Server [duplicate]

This question already has answers here:
SQL Multiple LIKE Statements
(3 answers)
Closed 5 years ago.
Assuming my table is T and my column is C, I need to check if any substring in C has any of these values: Orange,Apple,Banana,Kiwi,Strawberry,Blueberry.
My actual list of values is over 80 in length and I would like to try to avoid the following:
Select * from T
Where C LIKE '%Orange%'
OR C LIKE '%Apple%'
OR C LIKE '%Banana%'
OR C LIKE '%Kiwi%'
OR C LIKE '%Strawberry%'
OR C LIKE '%Blueberry%'
Is there a way to insert my values into a table and compare it to that? I make this comparison multiple times in my query.
This is one of those unusual situations where I would use a function to convert my comma separated list into a table and then join to it:
CREATE FUNCTION [dbo].[udf_ConvertStringArrayToTable] (
#StringArray VARCHAR(MAX)
)
RETURNS #StringTable TABLE
(
String VARCHAR(500)
)
AS
BEGIN
DECLARE #Index INT
SET #StringArray = LTRIM(RTRIM(#StringArray))
WHILE LEN(#StringArray) > 0
BEGIN
SET #Index = CHARINDEX(',', #StringArray, 1)
IF #Index = 0 BEGIN SET #Index = LEN(#StringArray) + 1 END
INSERT INTO #StringTable
SELECT SUBSTRING(#StringArray, 1, #Index - 1)
If #Index > LEN(#StringArray) BEGIN SET #StringArray = '' END
ELSE BEGIN SET #StringArray = SUBSTRING(#StringArray, #Index + 1, LEN(#StringArray) - #Index) END
END
RETURN
END
Then you could call it like this:
DECLARE #Example VARCHAR(MAX)
SET #Example = 'Orange,Apple,Banana,Kiwi,Strawberry,Blueberry' -- etc
SELECT
*
FROM
udf_ConvertStringArrayToTable(#Example) E
INNER JOIN T ON T.C LIKE '%' + E.String + '%'

Generate alphanumeric sequence with sql stored procedure

I need to Auto Generate Employee Reference number unique sequence with primary key. Sample Refno: (A0001-Z9999) now exeeded the maximum count(Z9999)
Next sequence I want to generate:
AA001-AA999,
AB001-AB999,
AZ999-BA001
BC001-BZ999,
CA001-CZ999,
ZA001-ZZ999..LIKE THIS..IN sql server stored procedure..
...ZZ999 LIKE THIS
This query will give you all values you need:
--here we take all the english alphabet
;WITH chars AS (
SELECT CHAR(65) as chars, 65 as [level]
UNION ALL
SELECT CHAR([level]+1), [level]+1
FROM chars
WHERE [level]-65 < 25
), cte AS ( -- Here we take didits from 1 to 999
SELECT 1 as digits
UNION ALL
SELECT digits+1
FROM cte
WHERE digits < 999
), codes AS ( -- here we get all chars combinations "AA", "AB" etc
SELECT c1.chars+c2.chars as code
FROM chars c1
cross join chars c2
)
--And here come cortesian join to get all refnomes ou need
SELECT code + CASE WHEN LEN(digits) = 1 THEN CONCAT('00',cast(digits as nvarchar(1)))
WHEN LEN(digits) = 2 THEN CONCAT('0',cast(digits as nvarchar(2)))
ELSE cast(digits as nvarchar(3)) END as Refno
FROM cte
CROSS JOIN codes
ORDER BY code
OPTION (maxrecursion 1000)
Output:
Refno
AA001
AA002
AA003
AA004
AA005
AA006
AA007
AA008
AA009
AA010
AA011
AA012
AA013
AA014
AA015
AA016
AA017
...
etc
ZZ999
~675324 rows
You can put the result into some table and in stored procedure return value you need from that table.
You can do it like this.
First create a SEQUENCE to generate integer sequence numbers starting from 1.
CREATE SEQUENCE dbo.MySeq AS INT
START WITH 1
INCREMENT BY 1;
Then create a stored procedure which generates a sequence code in the required format.
CREATE PROCEDURE dbo.up_GetNextSequence
(
#seq nchar(5) out
)
AS
DECLARE #i int = NEXT VALUE FOR dbo.MySeq;
IF #i > 685323
BEGIN
RAISERROR(N'Sequence is out of range.', 16, 0);
END
IF #i < 10000
BEGIN
SET #seq = N'A' + FORMAT(#i, 'D4');
END
ELSE
BEGIN
DECLARE #j int;
SET #j = #i - 9999;
DECLARE #k int;
SET #k = ((#j - 1) % 999) + 1;
DECLARE #l int;
SET #l = (#j - 1) / 999;
DECLARE #m int;
SET #m = (#l % 26) + 65;
DECLARE #n int;
SET #n = (#l / 26) + 65;
SET #seq = NCHAR(#n) + NCHAR(#m) + FORMAT(#k, 'D3');
END;
Then test the stored procedure.
DECLARE #seq nchar(5);
EXEC up_GetNextSequence #seq output;
SELECT #seq AS '#seq'
Call it four more times and we get to "A0005".
To test the first breakpoint, alter the sequence object so that it returns 9999. Then execute the test code again.
ALTER SEQUENCE dbo.MySeq
RESTART WITH 9999;
And so on...
ALTER SEQUENCE dbo.MySeq
RESTART WITH 10998;
ALTER SEQUENCE dbo.MySeq
RESTART WITH 35973;
ALTER SEQUENCE dbo.MySeq
RESTART WITH 659349;
To test what happens when it gets to the end of the allowed range.
ALTER SEQUENCE dbo.MySeq
RESTART WITH 685323;
Execute the stored procedure one more time and it raises an error, saying the sequence is out of range. This is by design.

How to split comma separated string in sql

Split only after comma 3 times appear in sql.
I have string that looks like this
"abc,123,855,jkl,ddd,rrr,sss,999,777,kkk,jkl,ddd,rrr,sss,999"
getting this from db.
What I want returned is a string[] that was split after every 3rd comma, so it would look like this:
abc,123,855,
jkl,ddd,rrr,
sss,999,777,
kkk,jkl,ddd,
rrr,sss,999
Need this field to display in my JSP page, currently it taking more space of the table row.
Would really appreciate any help I can get!
Here is the complete example.
DECLARE #valueList varchar(8000)
DECLARE #pos INT
DECLARE #len INT
DECLARE #value varchar(8000)
SET #valueList = 'Pakistan,UAE,USA,UK,'
set #pos = 0
set #len = 0
WHILE CHARINDEX(',', #valueList, #pos+1)>0
BEGIN
set #len = CHARINDEX(',', #valueList, #pos+1) - #pos
set #value = SUBSTRING(#valueList, #pos, #len)
SELECT #pos, #len, #value
set #pos = CHARINDEX(',', #valueList, #pos+#len) +1
END
Solution for MS SQL. Assuming that your string with commas is in column in a table and there could be more rows with more different strings. Also assuming that last string remaining after parsing could have less than 3 commas.
Using two loops, First loop is checking if there are still rows to process. Second inner loop is checking if there is still remaining string with commas to process.
Original table with comma string column TableWithCommaStrings
Final table in this case only temp #Temp_3, but you can insert it anywhere.
IF OBJECT_ID('tempdb..#Temp_1') IS NOT NULL DROP TABLE #Temp_1
SELECT *
INTO #Temp_1
FROM TableWithCommaStrings
;
DECLARE #RowCount AS INT = 1;
WHILE #RowCount >= 1
BEGIN
IF OBJECT_ID('tempdb..#Temp_2') IS NOT NULL DROP TABLE #Temp_2
SELECT TOP 1 * /* Pick Only First line */
INTO #Temp_2
FROM #Temp_1;
WITH AA AS (SELECT TOP 1 * FROM #Temp_1 ) /* Delete that first line from Temp1*/
DELETE FROM AA;
/* Parse Column till last comma character remain there*/
DECLARE #CommaCharCount AS INT = 3;
WHILE #CommaCharCount >= 3
BEGIN
IF OBJECT_ID('tempdb..#Temp_3') IS NULL CREATE TABLE #Temp_3 (YourStringColumn VARCHAR(MAX));
INSERT INTO #Temp_3 /* Insert substring into temp3 based on Third Comman from beginning*/
SELECT substring(AA.YourStringColumn,1,(CharIndex(',',AA.YourStringColumn,Charindex(',',AA.YourStringColumn,CharIndex(',',AA.YourStringColumn)+1) +1))-1)
FROM #Temp_2 AS AA
UPDATE BB /*Remove part of string which has been procssed already in step above*/
SET BB.YourStringColumn = substring(YourStringColumn,((CharIndex(',',YourStringColumn,Charindex(',',YourStringColumn,CharIndex(',',YourStringColumn)+1) +1)+1)+1),LEN(YourStringColumn))
FROM #Temp_2 AS BB
/* Set comma counter */
SELECT #CommaCharCount= LEN(YourStringColumn) - LEN(REPLACE(YourStringColumn, ',', '')) FROM #Temp_2
END
IF (SELECT LEN(YourStringColumn) FROM #Temp_2) > 0
INSERT INTO #Temp_3 /* Last remaining Characters */
SELECT YourStringColumn
FROM #Temp_2
SELECT #RowCount = COUNT(*) FROM #Temp_1;
END
You can accomplish this far more easily in JSP using JSTL. In fact, since your actual need is just of display, you don't even need to create an array. Here is how to do it using the <c:forTokens> JSTL tag.
<c:set var="sqlString" value="abc,123,855,jkl,ddd,rrr,sss,999,777,kkk" />
<c:forTokens var="token" varStatus="tokenCounter" items="${sqlString}" delims=",">
<c:out value="${token}" />,
<c:if test="${tokenCounter.count % 3 == 0}">
<br />
</c:if>
</c:forTokens>

Query not working fine in while loop

I have a While loop where I am trying to insert.
DECLARE #CurrentOffer int =121
DECLARE #OldestOffer int = 115
DECLARE #MinClubcardID bigint=0
DECLARE #MaxClubcardID bigint=1000
WHILE 1 = 1
BEGIN
INSERT INTO Temp WITH (TABLOCK)
SELECT top (100) clubcard from TempClub with (nolock) where ID between
#MinClubcardand and #MaxClubcard
declare #sql varchar (8000)
while #OldestOffer <= #CurrentOffer
begin
print #CurrentOffer
print #OldestOffer
set #sql = 'delete from Temp where Clubcard
in (select Clubcard from ClubTransaction_'+convert(varchar,#CurrentOffer)+' with (nolock))'
print (#sql)
exec (#sql)
SET #CurrentOffer = #CurrentOffer-1
IF #OldestOffer = #CurrentOffer
begin
-- my logic
end
end
end
My TempClub table always checks only with first 100 records. My TempClub table has 3000 records.
I need to check all my clubcard all 3000 records with ClubTransaction_121,ClubTransaction_120,ClubTransaction_119 table.
The SELECT query in line 8 returns only the top 100 items
SELECT top (100) clubcard from TempClub ...
If you want to retrieve all items, remove the top (100) part of your statement
SELECT clubcard from TempClub ...
In order to do batch type processing, you need to set the #MinClubcardID to the last ID processed plus 1 and include an ORDER BY ID to ensure that the records are being returned in order.
But... I wouldn't use the approach of using the primary key as my "index". What you're looking for is a basic pagination pattern. In SQL Server 2005+, Microsoft introduced the row_number() function which makes pagination a lot easier.
For example:
DECLARE #T TABLE (clubcard INT)
DECLARE #start INT
SET #start = 0
WHILE(1=1)
BEGIN
INSERT #T (clubcard)
SELECT TOP 100 clubcard FROM
(
SELECT clubcard,
ROW_NUMBER() OVER (ORDER BY ID) AS num
FROM dbo.TempClub
) AS t
WHERE num > #start
IF(##ROWCOUNT = 0) BREAK;
-- update counter
SET #start = #start + 100
-- process records found
-- make sure temp table is empty
DELETE FROM #T
END

SQL Query return values in a set sequence

I have been trying for a while now to return data from the database with the ID(int) values in the following order.
3, 6, 1, 9, 2, 5.
Is there anyway this can be done?
EDIT: Ok i made a bit of a stuff up in my post. the ID's above are just an example.
I am trying to do this dynamically, based around how many records from another table are linked to the record i want to pull out, e.g. i host 3 branches and each branch has a group of shops how would i determine which has the most?
I hope this helps.
Yes, something like this:
select ID from tablename
order by
CASE WHEN ID = 3 THEN 1
WHEN ID = 6 THEN 2
WHEN ID = 1 THEN 3
WHEN ID = 9 THEN 4
WHEN ID = 2 THEN 5
WHEN ID = 5 THEN 6
ELSE 7 END, ID ASC
This will put 3,6,1,9,2,5 and afterwords the other numbers in ascending order.
select cols from table where
order by
case ID when 3 then 0
when 6 then 1
when 1 then 2
when 9 then 3
...
end
You get the idea...
Create a table for the sorting.
CREATE TABLE SortPriority (
SourceID int NULL,
Priority int NULL)
Populate it with the ids and what order they should showup in. Join to the table. and use SortPriority.Priority in your sorting.
You can more easily change the sorting around this way. You would just need to modify the data. You can also later write scripts to populate the table to handle predictable needs in the changing of the sorting.
A split function like this one:
CREATE FUNCTION fnSplit(#str varchar(max), #dlm char(1))
RETURNS #result TABLE (id int, value varchar(50))
AS BEGIN
DECLARE
#id int, #value varchar(50),
#lastpos int, #pos int, #len int;
SET #id = 0;
SET #len = LEN(#str);
SET #lastpos = 1;
SET #pos = CHARINDEX(#dlm, #str + #dlm);
IF #pos <> 0
WHILE 1 = 1 BEGIN
SET #value = SUBSTRING(#str, #lastpos, #pos - #lastpos);
IF #value <> '' BEGIN
SET #id = #id + 1;
INSERT INTO #result VALUES (#id, #value);
END;
IF #pos > #len BREAK;
SET #lastpos = #pos + 1;
SET #pos = CHARINDEX(#dlm, #str + #dlm, #lastpos);
END;
RETURN;
END
would return a row set containing not only the values, but also their indexes within the list. You could then use the function in this way:
SELECT
…
FROM atable t
LEFT JOIN dbo.Split('3,6,1,9,2,5', ',') s ON t.Value = s.Value
ORDER BY
CASE WHEN s.id IS NULL THEN 2147483647 ELSE s.id END