Stored Procedure: Keep Query Result (as Table) in variable - sql

I'm from Entity Framework background and I don't do much pure SQL development.
However, when dealing with EF we can do:
var persons = from x in db.Person
where x.Name.Equals("Something")
select x;
or
IQuerable<Person> persons = from x in db.Person
where x.Name.Equals("Something")
select x;
But when dealing with stored procedures, how could this be done exactly, like for example I can capture a variable by declaring DECLARE #Name NVARCHAR(15), but how to get the returned table result?

Create a table variable and insert your data into that:
DECLARE #Person table(
FirstName nvarchar(15),
LastName nvarchar(15)
);
Insert Into #Person(FirstName, LastName)
Select Firstname, LastName
From Person
Where LastName = #LastName; -- where #lastname comes from a sp parameter.
Now do stuff like
Select count(*) from #Person;

simply declare a table variable:
DECLARE #TableVariable TABLE
(
PrimaryKey INT,
Name NVARCHAR(MAX)
)
and insert the results into this table.

One way as answered by #ps2goat. And if you want Stored Procedure output into table then you can do like this :-
DECLARE #Person table
(
Id Int Identity(1,1)
,FirstName nvarchar(15)
,LastName nvarchar(15)
);
Insert Into #Person(FirstName,LastName)
Exec dbo.USP_SomeStoredProcedure <sp's parameters>;
Select Top 1
FirstName
,LastName
From #Person As p
Order By p.Id Asc
Note:- your calling procedure output and table column exactly match should be same

Well I found the right answer myself. I think I have to use cursors and that is the best to do so.

Related

How to compare String Variable with Integer

I have this table structure and and some sample data. I want return data for multiple ids via parameter. I have declared a parameter string and now I want to compare it with the column but it ain't allowing because ID is integer.
Can anybody give me any help here ?
CREATE TABLE EMPLOYEE
(
ID INT,
EMPLOYEE_NAME VARCHAR(50)
);
INSERT INTO EMPLOYEE VALUES (1, 'Isaac Frempong');
INSERT INTO EMPLOYEE VALUES (2, 'Eric Ortizz');
DECLARE #StrID VARCHAR(20) = '1, 2'
SELECT * FROM EMPLOYEE
WHERE ID = #StrID
SELECT * FROM EMPLOYEE
WHERE #StrID+',' LIKE '%'+cast(ID as varchar(20))+'%,'
Pretty bad performance as it will need to do a table scan but safe enough.
Generally though, your list of IDs should be a table variable you can do a proper JOIN or IN with
The easiest solution is to use dynamic SQL
DECLARE #sql VARCHAR(1000) = 'SELECT * FROM EMPLOYEE WHERE ID IN (' + #StrID + ')';
EXEC(#sql);
For SQL Server 2017+ you could use STRING_SPLIT a table-valued function that splits a string into rows of substrings
CREATE TABLE EMPLOYEE
(
ID INT,
EMPLOYEE_NAME VARCHAR(50)
);
INSERT INTO EMPLOYEE VALUES (1, 'Isaac Frempong');
INSERT INTO EMPLOYEE VALUES (2, 'Eric Ortizz');
DECLARE #StrID VARCHAR(20) = '1, 2'
SELECT * FROM EMPLOYEE
WHERE ID IN (SELECT value FROM STRING_SPLIT (#StrID,','))
Refer this working fiddle
Create a user defined table type and pass it as a parameter.
CREATE TYPE [UDT_INTIDS] AS TABLE(
[ID] [int] NOT NULL
)
GO
-- create a table value
DECLARE #IDs [UDT_INTIDS];
INSERT #IDs VALUES (1),(2);
-- search using table value.
SELECT e.*
FROM EMPLOYEE e
WHERE e.ID IN (SELECT p.ID FROM #IDs p);
-- or
SELECT e.*
FROM EMPLOYEE e
JOIN #IDs p ON e.ID = p.ID;
See https://learn.microsoft.com/en-us/sql/relational-databases/tables/use-table-valued-parameters-database-engine?view=sql-server-2017 for more details.
You can use the Cast in SQL-Server to cast it to the appropriate datatype. Source Here
WHERE CAST(ID AS VARCHAR(20)) = #StrID
Alternatively: You can use CONVERT function.
WHERE CONVERT(VARCHAR(20), ID) = #StrID

Doing a for-each record in a user defined table type in Stored Procedure

:)
I have this defined type:
CREATE TYPE dbo.MyType
AS TABLE
(
name varchar(255),
value varchar(255)
);
Having this stored procedure:
CREATE PROCEDURE MyProcedure #docName varchar(255), #docPath varchar(255), #values AS [dbo].MyType Readonly
AS
declare #ID table (ID int)
INSERT INTO MyTable output inserted.fileID into #ID values (#docName,#docPath)
-- insert loop here
GO;
And the following "one to many" table
CREATE TABLE TableN (
fileID int PRIMARY KEY,
name varchar(255),
value varchar(255)
)
How can I, where it is noted in the above code, make a loop in order to for each record in the MyType table, to insert it into TableN, together with the fileID from the insert?
Thanks!
There's no need for a loop (you need to stop thinking programmatically in SQL and think in datasets). Just do an INSERT:
INSERT INTO TableN (FileID,[name],[value])
SELECT ID.ID,
V.[Name],
V.[value]
FROM #values v
CROSS JOIN #ID ID;

SQL Join Query Returning Null

I've got a query that references three tables.
The tables are as follows:
FORM_FACTOR
FORM_FACTOR_ID | FORM_FACTOR_DESCRIPTION
AOF_ORDER_LINE_QUEUE
SO_LINE_NUMBER | FORM_FACTOR_ID
AOF_ORDER_PARTS
SERIAL_NUMBER | SO_LINE_NUMBER
The idea is to get the FORM_FACTOR_DESCRIPTION using the SERIAL_NUMBER of the part. I wrote a query to do that, but for whatever reason it's returning null. I could swear this query was working, but apparently there is a flaw. Help is appreciated.
The query:
DECLARE #serial VARCHAR(50)
SET #serial = 'somestring'
SELECT
ff.[FORM_FACTOR_DESCRIPTION]
FROM
[dbo].[FORM_FACTOR] AS ff
JOIN
[dbo].[AOF_ORDER_LINE_QUEUE] AS lQ ON ff.[FORM_FACTOR_ID] = lQ.[FORM_FACTOR_ID]
JOIN
[dbo].[AOF_ORDER_PARTS] AS oO ON oO.[SO_LINE_NUMBER] = lQ.[SO_LINE_NUMBER]
WHERE
oO.[SERIAL_NUMBER] = #serial
Not really a solution here. It still remains an unsolved mystery at this point.
But to look for the root cause, perhaps you could turn the joins around and use left joins?
Perhaps it's then easier to spot at what point the joins return NULL on the right side.
In the example below I messed around with COLLATION.
Just ignore and remove it, most likely it has nothing to do with it.
DECLARE #serial VARCHAR(50);
SET #serial = 'EOXH2T100008' ;
SELECT DB_NAME() as current_db, DATABASEPROPERTYEX(DB_NAME(), 'Collation') as db_collation;
--#SQL_Latin1_General_CP1_CI_AS
--SELECT Name, Description FROM fn_helpcollations() where name like '%Latin1%' ;
declare #FORM_FACTOR table (FORM_FACTOR_ID int, FORM_FACTOR_DESCRIPTION varchar(30));
insert into #FORM_FACTOR (FORM_FACTOR_ID, FORM_FACTOR_DESCRIPTION) values (1,'test 1'),(2,'test 2');
declare #AOF_ORDER_LINE_QUEUE table (ORDER_LINE_QUEUE_ID int identity(1,1), FORM_FACTOR_ID int, SO_LINE_NUMBER int);
insert into #AOF_ORDER_LINE_QUEUE (FORM_FACTOR_ID, SO_LINE_NUMBER) values (1,100),(3,100),(2,200);
declare #AOF_ORDER_PARTS table (ORDER_PARTS_ID int identity(1,1), SO_LINE_NUMBER int, SERIAL_NUMBER VARCHAR(50) COLLATE SQL_Latin1_General_CP850_BIN);
insert into #AOF_ORDER_PARTS (SO_LINE_NUMBER, SERIAL_NUMBER) values (100,'EOXH2T100008'),(200,'eOXH2T100008');
SELECT
oO.ORDER_PARTS_ID, oO.SERIAL_NUMBER, oO.SO_LINE_NUMBER,
lQ.ORDER_LINE_QUEUE_ID, lQ.FORM_FACTOR_ID,
ff.FORM_FACTOR_DESCRIPTION
FROM #AOF_ORDER_PARTS AS oO
LEFT JOIN #AOF_ORDER_LINE_QUEUE AS lQ ON lQ.[SO_LINE_NUMBER] = oO.[SO_LINE_NUMBER]
LEFT JOIN #FORM_FACTOR AS ff ON ff.[FORM_FACTOR_ID] = lQ.[FORM_FACTOR_ID]
WHERE oO.[SERIAL_NUMBER] = #serial;

Insert data on a table from stored procedure resultset

I have a stored procedure that gets data from different tables. I want a few fields from the result set of the stored procedure to be inserted on another table.
How can I do this? Cursor, another stored procedure or what?
You can insert the result set from a stored procedure into another table, as in this example from this article:
DECLARE #People TABLE
(
ContactID INT,
FirstName NVARCHAR(50),
LastName NVARCHAR(50)
)
INSERT #People (ContactID, FirstName, LastName)
EXEC dbo.GetPeopleByLastName #LastName = 'Alexander'

SQL Server: Stored Proc input table variable workaround

I'm trying to find a good work around to not being able to use a table variable as an input to a stored procedure. I want to insert a single record into a base table and multiple records into a pivot table. My initial thought process led me to wanting a stored proc with separate inputs for the base table, and a single list input for the pivot table records, i.e.:
create proc insertNewTask (#taskDesc varchar(100), #sTime datetime, #eTime datetime, #items table(itemID int))
as
begin
declare #newTask table(newID int)
insert into tasks(description, sTimeUTC, eTimeUTC)
output inserted.ID into #newTask
values(#taskDesc, #sTime, #eTime)
insert into taskItems(taskID, itemID)
select newID, itemID
from #newTask cross join #items
end
As already stated, the above code won't work because of the table variable input, #items (I believe primarily due to variable scope issues). So, are there any good workarounds to this?
Original Question
I have three tables:
CREATE TABLE items
(
ID int PRIMARY KEY,
name varchar(20),
description varchar(100)
)
CREATE TABLE tasks
(
ID int identity(1,1) PRIMARY KEY,
description varchar(100),
sTimeUTC datetime,
eTimeUTC datetime
)
CREATE TABLE taskItems
(
taskID int,
itemID int,
CONSTRAINT fk_taskItems_taskID FOREIGN KEY (taskID) on tasks(ID),
CONSTRAINT fk_taskItems_itemID FOREIGN KEY (itemID) on items(ID)
)
With some initial item data:
insert into items (ID, name, description)
select 1, 'nails', 'Short piece of metal, with one flat side and one pointed side' union
select 2, 'hammer', 'Can be used to hit things, like nails' union
select 3, 'screws', 'I''m already tired of writing descriptions for simple tools' union
select 4, 'screwdriver', 'If you can''t tell already, this is all fake data' union
select 5, 'AHHHHHH', 'just for good measure'
And I have some code for creating a new task:
declare #taskDes varchar(100), #sTime datetime, #eTime datetime
select #taskDes = 'Assemble a bird house',
#sTime = '2011-01-05 12:00', #eTime = '2011-01-05 14:00'
declare #usedItems table(itemID int)
insert into #usedItems(itemID)
select 1 union
select 2
declare #newTask table(taskID int)
insert into tasks(description, sTimeUTC, eTimeUTC)
output inserted.ID into #newTask
values(#taskDes, #sTime, #eTime)
insert into taskItems(taskID, itemID)
select taskID, itemID
from #newTask
cross join #usedItems
Now, I want a way of simplifying/streamlining the creation of new tasks. My first thought was to use a stored proc, but table variables can't be used as inputs, so it won't work. I think I can do this with a view with an insert trigger, but I'm not sure... Is that my best (or only) option?
I have had great luck using XML to pass data to procedures. You can use OPENXML (Transact-SQL) to parse the XML.
-- You already had an example of #usedItems
-- declared and populated in the question
declare #usedItems table(itemID int)
insert into #usedItems(itemID)
select 1 union
select 2
-- Build some XML, either directly or from a query
-- Here I demonstrate using a query
declare #itemsXML nvarchar(max);
select #itemsXML =
'<Items>'
+ (select itemID from #usedItems as Item for xml auto)
+ '</Items>'
print #itemsXML
-- Pass #itemsXML to the stored procedure as nvarchar(max)
-- Inside the procedure, use OPENXML to turn the XML
-- back into a rows you can work with easily
DECLARE #idoc int
EXEC sp_xml_preparedocument #idoc OUTPUT, #itemsXML
SELECT *
FROM OPENXML (#idoc, '/Items/Item',1)
WITH (itemID int)
EXEC sp_xml_removedocument #idoc
Results
<Items><Item itemID="1"/><Item itemID="2"/></Items>
itemID
-----------
1
2