Loop through rows of a query in T-SQL? - sql

I have the following T-SQL script:
declare #Name nvarchar
declare data cursor for
select Name from MyDB.dbo.MyTable;
OPEN data;
-- Perform the first fetch.
FETCH NEXT FROM data;
-- Check ##FETCH_STATUS to see if there are any more rows to fetch.
WHILE ##FETCH_STATUS = 0
BEGIN
-- This is executed as long as the previous fetch succeeds.
FETCH NEXT FROM data INTO #Name;
Print 'Name: ' + #Name
END
CLOSE data;
DEALLOCATE data;
GO
I want to make a script that will compare each of the strings in a first column with each of the strings in the second column.
The problem is, I don't know how to loop through each of the rows and take a separate string value.
The code above prints only the first value in the query result.
What am I doing wrong?

To compare all values from one column to all values in another column you don't need a cursor, a simple join will do the work - since you didn't provide sample data and also not desired results, I had to make my own:
Create and populate sample table (Please save us this step in your future questions)
CREATE TABLE MyTable
(
Id int identity(1,1),
Name1 char(3),
Name2 char(3)
)
INSERT INTO MyTable (Name1, Name2) VALUES
('abc','def'),('zyx','abc'),
('ghi','jkl'),('yza','ghi'),
('mno','pqr'),('nml','mno'),('pqr','qpo'),
('stu','vwx'),('wvu','tsr'),('kji','hgf')
The query:
SELECT T1.Id, T1.Name1, T1.Name2, T2.Id, T2.Name1, T2.Name2
FROM MyTable T1
JOIN MyTable T2 ON T1.Name1 = T2.Name2
Result:
Id Name1 Name2 Id Name1 Name2
1 abc def 2 zyx abc
3 ghi jkl 4 yza ghi
5 mno pqr 6 nml mno
7 pqr qpo 5 mno pqr

You probably don't want to use a Cursor.
Are your columns in the same table? If so this is as simple as this;
-- Show All rows with [DIFFERENT] Name and Name2 fields
SELECT
Name,
Name2
FROM [MyDB].[dbo].[MyTable]
WHERE
Name <> Name2
-- Show All rows with [SAME] Name and Name2 fields
SELECT
Name,
Name2
FROM [MyDB].[dbo].[MyTable]
WHERE
Name = Name2
If not you will need to post the table definitions and names of columns to get a more concrete example

Related

SQL Server: Concat Rows / or Columns and update result to Column

Following Situation:
I have a dynamically (by columns) builded table ... eg:
Rowid UniqueID Name Birthdate Town ....
1 null Joe Jan-93 Cologne
2 null Nick Okt-00 London
I am building this TempTable to create an uniqueID for all Data in my DataBase
The TempTable was created by two loops which run through all my DataBase Table & Columns and copy all primary key Data to this TempTable.
My aim is to update the UniqueID Column from my TempTable with the concat values of data ... eg:
UniqueID
JoeJan-93Cologne
NickOkt-00London
Do you have an idea how to update UniqueID ?
What I m thinking about is:
Loop 1 going through all Tables
Select Table of Schema
Loop 2 going through all Columns of Table
Select Column of Schema
Copy Column to my Temp
-- here an update like ... set UniqueID = select concat(UniqueID, #Column)
-- from #table where RowID = RowID
End loop 2
end loop 1
Is this possible
Or do I have to open a third loop which is running through all rows and concat values ?
You can try this
Update <YourTableName>
set UniqueId = ISNULL(Name, '') + ISNULL(Cast(Birthdate as Varchar(10), '') + ISNULL(Town, '')
You can use CONCAT() with UPDATE statement, no any loop required :
UPDATE t
SET UniqueID = CONCAT(Name, Birthdate, Town);

Passing space delimited string to stored procedure to search entire database

How can I pass a string space delimited to a stored procedure and filter the result?
I'm trying to do this
parameter value
__________________
#query key1 key2 key 3
Then in the stored procedure, I want to first
find all the results with key1.
filter step 1 with key2.
filter step2 with key3.
Another example:
col1 | col2 | col3
------------+-----------------------+-----------------------------------
hello xyz | abc is my last name | and I'm a developer
hello xyz | null | and I'm a developer
If I search for any following it should return for each?
"xyz developer" returns 2 rows
"xyz abc" returns 1 row
"abc developer"returns 1 row
"hello" returns 2 rows
"hello developer" returns 2 rows
"xyz" returns 2 rows
I'm using SQL Server 2016. I tried to use split_string to split query string. But I don't know how to pass this to the stored procedure.
Thanks in advance
Full Text Index is the way to go, but this will return your results.
One caveat (that I can think of). If your search expression/pattern contains a column name, that will generate a false-positive
Declare #YourTable table (col1 varchar(50),col2 varchar(50),col3 varchar(50))
Insert Into #YourTable values
('hello xyz','abc is my last name','and I''m a developer'),
('hello xyz', null ,'and I''m a developer')
Declare #Search varchar(max) = 'xyz abc'
Select A.*
From #YourTable A
Cross Apply (Select FullString=(Select A.* FOR XML Raw)) B
Where FullString like '%'+replace(#Search,' ','%')+'%'
Returns
col1 col2 col3
hello xyz abc is my last name and I'm a developer
EDIT - Multi-Word / Any Order Search
Try this not fully tested. I can't imagine this being very efficient especially with larger tables and numerous key words
Declare #YourTable table (col1 varchar(50),col2 varchar(50),col3 varchar(50))
Insert Into #YourTable values
('hello xyz','abc is my last name','and I''m a developer'),
('hello xyz', null ,'and I''m a developer')
Declare #Search varchar(max) = 'developer xyz'
Select *
From (
Select Distinct A.*
,Hits = sum(sign(charindex(C.Value,B.FullString))) over (partition by B.FullString)
,Req = C.Req
From #YourTable A
Cross Apply (Select FullString=(Select A.* FOR XML Raw)) B
Join (Select *,Req=sum(1) over () From String_Split(#Search,' ') ) C on charindex(C.Value,B.FullString)>0
) A
Where Hits=Req
http://dbfiddle.uk/?rdbms=sqlserver_2016&fiddle=c77123a71c810716b36d73a92ac714eb

Sorting results of SQL query just like IN parameter list

I'm doing a query which looks something like
SELECT id,name FROM table WHERE id IN (2,1,4,3)
I'd like to get
id name
2 B
1 A
4 D
3 C
but I'm getting
1 A
2 B
3 C
4 D
Is there any way to sort the query results in the same way as the list I'm including after IN?
Believe me, I have a practical reason that I would need it for ;)
SELECT id,name FROM table WHERE id IN (2,1,4,3)
ORDER BY CASE id
WHEN 2 THEN 1
WHEN 1 THEN 2
WHEN 4 THEN 3
WHEN 3 THEN 4
ELSE 5
END
This might solve your problem.
Solution 2, insert your list into a temp table and get them a running sequence
id, seq(+1 every new row added)
-----------------
2 1
1 2
4 3
3 4
then join 2 table together and order by this seq.
Okay, I did it myself. It's a bit mad but it works ;)
DECLARE #IDs varchar(max)
DECLARE #nr int
DECLARE #znak varchar(1)
DECLARE #index int
DECLARE #ID varchar(max)
SET #IDs='7002,7001,7004,7003'
SET #nr=1
SET #index=1
IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp
CREATE TABLE #temp (nr int, id int)
--fill temp table with Ids
WHILE #index<=LEN(#Ids)
BEGIN
set #znak=''
set #ID=''
WHILE #znak<>',' AND #index<=LEN(#Ids)
BEGIN
SET #znak= SUBSTRING(#IDs,#index,1)
IF #znak<>',' SET #ID=#ID+#znak
SET #index=#index+1
END
INSERT INTO #temp(nr,id) VALUES (#nr,CAST(#ID as int))
SET #nr=#nr+1
END
-- select proper data in wanted order
SELECT MyTable.* FROM MyTable
INNER JOIN #temp ON MyTable.id=#temp.id
ORDER BY #temp.nr

SQL Server execute stored procedure in update statement

every updated record must have different value by using a procedure
the procedure returns single integer value
declare #value int;
exec #value = get_proc param;
update table1 set field1 = #value;
this will work for one record but i want the procedure to get new value for each record
Just a quick example of how to use a TVF to perform this type of update:
USE tempdb;
GO
CREATE TABLE dbo.Table1(ID INT, Column1 INT);
INSERT dbo.Table1(ID)
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3;
GO
CREATE FUNCTION dbo.CalculateNewValue
(#ID INT)
RETURNS TABLE
AS
-- no idea what this logic would be,
-- just showing an example
RETURN(SELECT NewValue = #ID + 1);
GO
SELECT t1.ID, n.NewValue
FROM dbo.Table1 AS t1
CROSS APPLY dbo.CalculateNewValue(t1.ID) AS n;
Results:
ID NewValue
-- --------
1 2
2 3
3 4
Now an update that uses the same information:
UPDATE t1 SET Column1 = n.NewValue
FROM dbo.Table1 AS t1
CROSS APPLY dbo.CalculateNewValue(t1.ID) AS n;
SELECT ID, Column1 FROM dbo.Table1;
Results:
ID Column1
-- -------
1 2
2 3
3 4
Does it really need to be a procedure ? If you can implement get_proc as function, then you can apply it on every record that you want ;)
Also, what are you using value1 for ? In the example that you've provided, it's not needed.

Passing delimited string to stored procedure to search database

How can i pass a string delimited by space or comma to stored procedure and filter result?
I'm trying to do something like -
Parameter Value
--------------------------
#keywords key1 key2 key3
Then is stored procedure i want to first
find all records with first or last
name like key1
filter step 1 with first or last
name like key2
filter step 2 with first or last name like key 3
Another example:
col1 | col2 | col3
------------------------------------------------------------------------
hello xyz | abc is my last name | and i'm a developer
hello xyz | null | and i'm a developer
If i search for any following it should return for each?
"xyz developer" returns 2 rows
"xyz abc" returns 1 row
"abc developer"returns 1 row
"hello" returns 2 rows
"hello developer" returns 2 rows
"xyz" returns 2 rows
Since you can't use a table parameter (not on SQL Server 2008), try passing in a CSV sting and have the stored procedure split it into rows for you.
There are many ways to split string in SQL Server. This article covers the PROs and CONs of just about every method:
"Arrays and Lists in SQL Server 2005 and Beyond, When Table Value Parameters Do Not Cut it" by Erland Sommarskog
You need to create a split function. This is how a split function can be used:
SELECT
*
FROM YourTable y
INNER JOIN dbo.yourSplitFunction(#Parameter) s ON y.ID=s.Value
I prefer the number table approach to split a string in TSQL but there are numerous ways to split strings in SQL Server, see the previous link, which explains the PROs and CONs of each.
For the Numbers Table method to work, you need to do this one time table setup, which will create a table Numbers that contains rows from 1 to 10,000:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Once the Numbers table is set up, create this split function:
CREATE FUNCTION [dbo].[FN_ListToTable]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN
( ----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
SELECT
ListValue
FROM (SELECT
LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(#SplitOn, List2, number+1)-number - 1))) AS ListValue
FROM (
SELECT #SplitOn + #List + #SplitOn AS List2
) AS dt
INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
WHERE SUBSTRING(List2, number, 1) = #SplitOn
) dt2
WHERE ListValue IS NOT NULL AND ListValue!=''
);
GO
You can now easily split a space delimited string into a table and join on it or use it however you need This codes is based on the OPs latest question edit:
CREATE TABLE YourTable (PK int, col1 varchar(20), col2 varchar(20), col3 varchar(20))
--data from question
INSERT INTO YourTable VALUES (1,'hello xyz','abc is my last name','and i''m a developer')
INSERT INTO YourTable VALUES (2,'hello xyz',null,'and i''m a developer')
CREATE PROCEDURE YourProcedure
(
#keywords varchar(1000)
)
AS
SELECT
#keywords AS KeyWords,y.*
FROM (SELECT
t.PK
FROM dbo.FN_ListToTable(' ',#keywords) dt
INNER JOIN YourTable t ON t.col1 LIKE '%'+dt.ListValue+'%' OR t.col2 LIKE '%'+dt.ListValue+'%' OR t.col3 LIKE '%'+dt.ListValue+'%'
GROUP BY t.PK
HAVING COUNT(t.PK)=(SELECT COUNT(*) AS CountOf FROM dbo.FN_ListToTable(' ',#keywords))
) dt
INNER JOIN YourTable y ON dt.PK=y.PK
GO
--from question
EXEC YourProcedure 'xyz developer'-- returns 2 rows
EXEC YourProcedure 'xyz abc'-- returns 1 row
EXEC YourProcedure 'abc developer'-- returns 1 row
EXEC YourProcedure 'hello'-- returns 2 rows
EXEC YourProcedure 'hello developer'-- returns 2 rows
EXEC YourProcedure 'xyz'-- returns 2 rows
OUTPUT:
KeyWords PK col1 col2 col3
-------------- ----- ---------- -------------------- --------------------
xyz developer 1 hello xyz abc is my last name and i'm a developer
xyz developer 2 hello xyz NULL and i'm a developer
(2 row(s) affected)
KeyWords PK col1 col2 col3
-------------- ----- ---------- -------------------- --------------------
xyz abc 1 hello xyz abc is my last name and i'm a developer
(1 row(s) affected)
KeyWords PK col1 col2 col3
-------------- ----- ---------- -------------------- --------------------
abc developer 1 hello xyz abc is my last name and i'm a developer
(1 row(s) affected)
KeyWords PK col1 col2 col3
-------------- ----- ---------- -------------------- --------------------
hello 1 hello xyz abc is my last name and i'm a developer
hello 2 hello xyz NULL and i'm a developer
(2 row(s) affected)
KeyWords PK col1 col2 col3
--------------- ----- ---------- -------------------- --------------------
hello developer 1 hello xyz abc is my last name and i'm a developer
hello developer 2 hello xyz NULL and i'm a developer
(2 row(s) affected)
KeyWords PK col1 col2 col3
-------------- ----- ---------- -------------------- --------------------
xyz 1 hello xyz abc is my last name and i'm a developer
xyz 2 hello xyz NULL and i'm a developer
(2 row(s) affected)
You could try something like:
select firstname, lastname from #test t1
inner join persondata t on t.firstname like '%' + t1.x + '%' or t.lastname like '%' + t1.x + '%'
group by firstname, lastname
having count(distinct x) = (select count(*) from #test)
where #test is the table with results of your split. If you have lots of columns in persondata, you might want to just return an ID from this query, and use it as a subquery for the one that actually returns data, so you don't have to group by so many columns.
Edit: you could also use a cursor and another temp table/table variable, but I have kind of an allergic reaction to cursors in SPs.