SQL Server 2008 R2: Cursor with condition - sql-server

I have the table which contains the table's id and name.
Now I want to retrieve the table names by passing table ID to the stored procedure.
Example:
create procedure spGetAllTables
#TableIDs varchar(max)
AS
DECLARE #Tables varchar(max)
DECLARE Cur1 CURSOR FAST_FORWARD FOR
SELECT TableName FROM TablesContainer
WHERE TableID IN (#TableIDs)
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #Tables
WHILE(##FETCH_STATUS=0)
BEGIN
PRINT(#Tables)
FETCH NEXT FROM Cur1 INTO #Tables
END
CLOSE Cur1;
DEALLOCATE Cur1;
GO
Explanation: The table TablesContainer contains all table's ID's and table's names. I just want to print all those table names which I have passed their table ID's to the stored procedure.
The procedure working fine if I pass single value to the variable #TableIDs. But If I pass multiple values like 1,2,3 to #TableIDs then its not getting entered into the cursor.

i think you don't need cursor, there is multiple way to separate string you can pass table value parameter and split the string but as per the Q requirement Reference
DECLARE #temp TABLE (Id INT)
INSERT INTO #temp VALUES (1),(2),(6)
DECLARE #TableIDs varchar(max),
#xml xml
SELECT #TableIDs = '1,2,3,4,5'
SELECT #xml = CONVERT(xml,' <root> <x>' + REPLACE(#TableIDs,',','</x> <x>') + '</x> </root> ')
SELECT id FROM #temp
WHERE id IN (
SELECT T.c.value('.','INT')
FROM #xml.nodes('/root/x') T(c)
)

You don't neet to pass the IDs as a CSV list. SQL Server has table-valued parameters since the SQL Server 2008 version at least. A TVP is a table parameter to which you insert data with plain INSERT statements in SQL, or as an collection (a DataTable or DataReader) in ADO.NET.
You can JOIN with the TVP as you would with any table or table variable.
First, you need to define the TVP type in the database:
CREATE TYPE IdTableType AS TABLE ( ID uniqueidentifier);
Then you can write a stored procedure to use it:
create procedure spGetAllTables(#TableIDs IdTableType READONLY)
AS
SELECT TableName
FROM TablesContainer
inner join #TableIDs on #TableIDs.ID=TablesContainer.TableID
To call the stored procedure from T-SQL you can create a table parameter and fill it:
declare #tableIds IdTableType;
insert into #tableIds
VALUES
('....'),
('....');
exec #spGetAllTables(#tableIds);

Related

Table valued parameters join performance

I want to pass a list of names to a stored procedure and then perform a left join. I have passed the list of names as a table-valued parameter.
CREATE PROCEDURE [DBO].[INSERTANDGETLATESTNAMES]
(#list [dbo].[NamesCollection] READONLY)
AS
BEGIN
INSERT INTO [dbo].[Employee](NAME)
OUTPUT INSERTED.NAME
SELECT NamesCollection.Name
FROM #list AS NamesCollection
LEFT JOIN [dbo].[Employee] AS emp ON NamesCollection.Name = emp.Name
WHERE emp.Name IS NULL
END
User-defined table type:
CREATE TYPE [dbo].[NamesCollection] AS TABLE
(
[NAME] [varchar](50) NULL
)
GO
SQL Server does not maintain statistics on table-valued parameters will that effect join performance in above case. If performance is slow then can I go for passing the list of names in comma separated string and write a function to split and return a table to the stored procedure?
CREATE FUNCTION split_string_XML
(#in_string VARCHAR(MAX),
#delimiter VARCHAR(1))
RETURNS #list TABLE(NAMES VARCHAR(50))
AS
BEGIN
DECLARE #sql_xml XML = Cast('<root><U>'+ Replace(#in_string, #delimiter, '</U><U>')+ '</U></root>' AS XML)
INSERT INTO #list(NAMES)
SELECT f.x.value('.', 'VARCHAR(50)') AS NAMES
FROM #sql_xml.nodes('/root/U') f(x)
WHERE f.x.value('.', 'VARCHAR(50)') <> ''
RETURN
END
GO
or
CREATE FUNCTION split_string_delimiter
(#in_string VARCHAR(MAX),
#delimiter VARCHAR(1))
RETURNS #list TABLE(NAME VARCHAR(50))
AS
BEGIN
INSERT INTO #list(NAME)
SELECT value AS NAMES
FROM STRING_SPLIT(#in_string, #delimiter);
RETURN
END
GO
Using STRING_SPLIT is better then using XML PATH for splitting. If you can use STRING_SPLIT, you can use JSON PATH.
Using JSON PATH, transform the JSON variable into row set and insert it #temporary table, not #table variable as depending on your SQL Server version, #temporary tables performed better when large amount of data is proceed.
Also, if you want to add new fields to the JSON variable there will be no need to edit the table type. While editing the table type is difficult because of referencing.
The SELECT in which you're using the table-valued parameter is pretty straightforward.
It's just a join and given that the cardinality for table variables is calculated as one, assuming that dbo.Employee.Name is indexed and that column types match, that join is going to be implemented with a loop join that is the quickest option for that case.
Just make sure that dbo.Employee.Name is properly indexed.

Execute 2 queries that are in 2 variables and store the result in other 2 variables

As mentioned in the question, I want to store the results of these queries into other variables so that I can compare the output and find the ones which are not matching. Please help me out with it. The variable #Stagename consists of the first query and variable #correctname consists of the second query. I found some answers of storing them into a table variable but it is not working. These queries are not a single query and hence are stored in the form of rows of a table and are being fetched by the cursor. I've passed the second variable #tablename which I want as the final output but only of the ones in whom the comparison is not matching. I've used the following code:
DECLARE #Stagename VARCHAR(MAX)
DECLARE #correctname VARCHAR(MAX)
DECLARE #tablename VARCHAR(MAX)
--DECLARE #StageCount VARCHAR(max)
--DECLARE #IndexCount VARCHAR(MAX)
DECLARE #Table1 TABLE (StageCount Varchar(max), TableName VARCHAR(MAX))
DECLARE #Table2 TABLE (IndexCount Varchar(max), TableName VARCHAR(MAX))
--DEALLOCATE IF EXISTS CS_StagingIndex
DECLARE CS_StagingIndex CURSOR FOR
SELECT StageCount, CorrectCount, TableName FROM bak.StagingindexesQuery
OPEN CS_StagingIndex
FETCH NEXT FROM CS_StagingIndex
INTO #Stagename,#Correctname,#tablename
WHILE ##Fetch_Status = 0
BEGIN
INSERT INTO #Table1(StageCount,TableName) VALUES (exec(#StageName),#tablename);
INSERT INTO #Table2(IndexCount,TableName) VALUES (exec(#CorrectName),#tablename);
--Select * from #Table1 Ignore this.
FETCH NEXT FROM CS_StagingIndex
INTO #Stagename,#Correctname,#tablename
END
CLOSE CS_StagingIndex
DEALLOCATE CS_StagingCursor
Select count(1) from stg.LogisticsElectronicAddres - This is the query stored in #StageName.
select count(1) from (select distinct recid1 from stg.LogisticsElectronicAddress) x - This is the query stored in #IndexName.
LogisticsElectronicAddress and this is the tablename.
Now if for example, the result of StageName query is 2000 and the result of Correctname is also 2000, then the tablename should not be printed. But if the results dont match, then I want the tablename to be printed. There are multiple rows in bak.StagingIndexesQuery table that contain such queries for multiple tables.
I don't believe that's the correct EXEC syntax for sql-server; I don't think that is going to work.
What you can do is use the statement .. INSERT table EXEC storedprocName.
However, there are constraints - the table layout must match that of the return procedure in terms of column count/order/data types/length ( within reason, ie if a column in the table is NVARCHAR(100) and the stored procedure returns that column as NVARCHAR(105), that should be fine - unless of course the data itself exceeds the 100 length). I believe column names are ignored for INSERT/EXEC
Also, if the query being executed has an INSERT/EXEC , this will not work (only one allowed at anyone time)
So you will need to use dynamic SQL...
DECLARE #MySQLStatement NVARCHAR(400)
DECLARE #MyStoedProcName NVARCHAR(100)
DECLARE #Table1 TABLE (StageCount Varchar(max), TableName VARCHAR(MAX))
SET #MySQLStatement = 'INSERT #Tablename EXEC ' + #StoredProcedure
EXEC sp_ExecuteSQL #MySQLStatement
Now off the top of my head, I can't remember if that #Table1 will be in the scope of that dynamic SQL statement. If it isn't, make #Table1 a temp table (ie create table #table1 [ ... etc ] )

Add column into Table through variable using SQL Server

I have check unique value in the column VariantSKU using this sql code
alter Proc spIsUnique
#columnname nvarchar(max),
#tablename nvarchar(max)
As
Begin
EXEC ('select '+#columnname+',
IIf (count(*)>1,''False'',''True'') as Total
from '+#tablename+'
group by '+#columnname)
End
As you can see new column Total which contain True or False..
Now I want to add this column into the table in database. USing function it was not possible so I have created new table exactly same data called "Result" table.How can I add that column Total in Result table.
How can I do it?
if i understand well your question then use temp db to stock your table then join it with the destination table or do juste a union
alter Proc spIsUnique
#columnname nvarchar(max),
#tablename nvarchar(max)
As
Begin
= EXEC ('select '+#columnname+',
IIf (count(*)>1,''False'',''True'') as Total
into ##tempdb
from '+#tablename+'
group by '+#columnname)
End
select d.* , t.Total from destinationtable as d
inner join ##tempdb as t ON d.#columnname = t.#columnname

Accessing temp table outside Stored Procedure

I have Stored Procedure which gives output as temp table
Create Proc Hello (#id int,#name nvarchar(30))
as
begin
If (OBJECT_ID('tempdb..#Welcome') Is Not Null) Drop Table #Welcome
select * into #Welcome from hello where id=#id
If (OBJECT_ID('tempdb..#Welcomes') Is Not Null) Drop Table #Welcomes
select * into #Welcomes from hello where name=#name
end
Now I got 2 temp table as result which i will be using in dataset..
Now i need to access the this #welcome in another stored procedure ..I mean
Create Proc HelloThere(#ids int,#name nvarchar(10))
as
begin
exec hello #id = #ids ,#name =#name
//select * from #Welcome(Here i need to access the #Welcome so i can perform inner join something like below//
select * from #welcome inner join Atable on #welcome.id=Atable.id
end
Temporary tables created in a stored procedure are automatically dropped when the stored procedure completes, so the temp table will not be available to the calling stored procedure.
One method to keep the temp table around is to explicitly create the table in the calling proc (using CREATE TABLE or SELECT INTO) and then load it in the called proc:
CREATE PROC Hello #id int,#name nvarchar(30)
AS
INSERT INTO #Welcome SELECT * FROM hello where id=#id;
GO
CREATE PROC HelloThere(#ids int,#name nvarchar(10))
AS
If OBJECT_ID(N'tempdb..#Welcome', 'U') IS NOT NULL DROP TABLE #Welcome;
SELECT * INTO #welcome FROM hello WHERE 0 = 1;
EXEC hello #id = #ids ,#name =#name;
SELECT * FROM #welcome INNER JOIN Atable ON #welcome.id=Atable.idl
GO
As #JeroenMostert mentioned in comments, you can peruse http://www.sommarskog.se/share_data.htmlHow to Share Data between Stored Procedures for other techniques.
You can create a global temporary table (2 "#") if need to use outside of the procedure:
CREATE PROC TEST AS
SELECT TOP 10 * INTO ##GLOBAL_TABLE FROM SYS.all_objects
GO
EXEC TEST
SELECT * FROM ##GLOBAL_TABLE --It works
Just be careful that other processes that may try to use the same name and generate errors (or drop your global table and use it in another way)
You can create the table with the date you've created the procedure after (Change ##USERS to something like ##USERS_20220101)

Insert list of tree columns into SQL Server stored procedure

I have a table Zone that contains these columns:
ID
Name
Province_Id
City_Id
I want to pass a list of 3 columns and insert them into that table with a SQL Server stored procedure.
Like this
Name 'a,b,c,d'
Province_Id 1,2,3,4
City_Id 1,2,3,4
I write this procedure but this procedure works only for one column
CREATE PROCEDURE [dbo].[CreateZone]
#Name NVARCHAR(50),
#BuildingProvince_Id INT,
#BuildingCity_Id INT
AS
BEGIN
BEGIN TRY
DECLARE #xml xml;
SET #xml = cast(('<X>'+replace(#Name,',' ,'</X><X>')+'</X>') as xml)
INSERT INTO ZoneCategory
SELECT N.value('.', 'varchar(100)') as value
FROM #xml.nodes('X') as T(N)
SELECT 100
END TRY
BEGIN CATCH
EXEC InsertSQLError
SELECT -100 as Error
END CATCH
END
How can I change it to insert values for 3 columns?
Or create a procedure for this scenario?
I want insert values for 3 columns with comma
What you're attempting to do is pass an array (well, multiple arrays) as input parameters to a stored procedure.
With that understanding, rather than reword / plagiarize what is already an excellent answer, I'll just point you at this...
How to pass an array into a SQL Server stored procedure

Resources