SQL Query Results to Local Variable without unique identifier - sql-server

I'm relatively new to SQL and I'm trying to write a query that will assign the result of multiple rows into a local variable
DECLARE #x VARCHAR(MAX)
SET #x= (SELECT someCol
FROM table)
--Does important stuff to the #x variable
SELECT #x
During my research I realized that this won't work because the subquery can only return one value and my query will return multiple results. However I can not do something like this:
DECLARE #x VARCHAR(MAX)
SET #x= (SELECT someCol
FROM table
where id= 'uniqueIdentifier')
--Does important stuff to the #x variable
SELECT #x
The reason I can't use a where clause is that I need to do this for the entire table and not just one row. Any ideas?
EDIT: I realized my question was too broad so I'll try to reformat the code to give some context
SELECT col_ID, col_Definition
FROM myTable
If I were to run this query col_Definition would return a large varchar which holds a lot of information such as the primary key of another table that I'm trying to obtain. Lets say for example I did:
DECLARE #x VARCHAR(MAX)
SET #x= (SELECT col_Definition
FROM myTable
WHERE col_ID = 1)
--Function to do the filtering of the varchar variable that works as expected
SELECT #x as [Pk of another table] --returns filtered col_Definition
This will work as expected because it returns a single row. However, I would like to be able to run this query so that it will return the filtered varchar for every single row in the "myTable" table.

If I understand correctly, you store a PK embedded in a string that you want to eventually get out and use to join to that table. I would think putting the group of records you want to work with into a temp table and then applying some logic to that varchar column to get the PK. That logic is best as set based, but if you really want row by row, use a scalar function rather than a variable and apply it to the temp table:
select pk_column, dbo.scalarfunction(pk_column) as RowByRowWithFunction, substring(pk_column,5,10) as SetBasedMuchFaster
from #tempTable.

You need to define what the 'uniqueIdentifier' is first.
Not sure about using a subquery and grabbing the result and executing another query with that result unless you do an INNER JOIN of some sort or if using python or another language to process the data then use:
("SELECT someCol
FROM table
where id='%s'" % Id)

Related

Use output of procedure in a new procedure SQL Server [duplicate]

I'm not sure if this is something I should do in T-SQL or not, and I'm pretty sure using the word 'iterate' was wrong in this context, since you should never iterate anything in sql. It should be a set based operation, correct? Anyway, here's the scenario:
I have a stored proc that returns many uniqueidentifiers (single column results). These ids are the primary keys of records in a another table. I need to set a flag on all the corresponding records in that table.
How do I do this without the use of cursors? Should be an easy one for you sql gurus!
This may not be the most efficient, but I would create a temp table to hold the results of the stored proc and then use that in a join against the target table. For example:
CREATE TABLE #t (uniqueid int)
INSERT INTO #t EXEC p_YourStoredProc
UPDATE TargetTable
SET a.FlagColumn = 1
FROM TargetTable a JOIN #t b
ON a.uniqueid = b.uniqueid
DROP TABLE #t
You could also change your stored proc to a user-defined function that returns a table with your uniqueidentifiers. You can joing directly to the UDF and treat it like a table which avoids having to create the extra temp table explicitly. Also, you can pass parameters into the function as you're calling it, making this a very flexible solution.
CREATE FUNCTION dbo.udfGetUniqueIDs
()
RETURNS TABLE
AS
RETURN
(
SELECT uniqueid FROM dbo.SomeWhere
)
GO
UPDATE dbo.TargetTable
SET a.FlagColumn = 1
FROM dbo.TargetTable a INNER JOIN dbo.udfGetUniqueIDs() b
ON a.uniqueid = b.uniqueid
Edit:
This will work on SQL Server 2000 and up...
Insert the results of the stored proc into a temporary table and join this to the table you want to update:
INSERT INTO #WorkTable
EXEC usp_WorkResults
UPDATE DataTable
SET Flag = Whatever
FROM DataTable
INNER JOIN #WorkTable
ON DataTable.Ket = #WorkTable.Key
If you upgrade to SQL 2008 then you can pass table parameters I believe. Otherwise, you're stuck with a global temporary table or creating a permanent table that includes a column for some sort of process ID to identify which call to the stored procedure is relevant.
How much room do you have in changing the stored procedure that generates the IDs? You could add code in there to handle it or have a parameter that lets you optionally flag the rows when it is called.
Use temporary tables or a table variable (you are using SS2005).
Although, that's not nest-able - if a stored proc uses that method then you can't dumpt that output into a temp table.
An ugly solution would be to have your procedure return the "next" id each time it is called by using the other table (or some flag on the existing table) to filter out the rows that it has already returned
You can use a temp table or table variable with an additional column:
DECLARE #MyTable TABLE (
Column1 uniqueidentifer,
...,
Checked bit
)
INSERT INTO #MyTable
SELECT [...], 0 FROM MyTable WHERE [...]
DECLARE #Continue bit
SET #Continue = 1
WHILE (#Continue)
BEGIN
SELECT #var1 = Column1,
#var2 = Column2,
...
FROM #MyTable
WHERE Checked = 1
IF #var1 IS NULL
SET #Continue = 0
ELSE
BEGIN
...
UPDATE #MyTable SET Checked = 1 WHERE Column1 = #var1
END
END
Edit: Actually, in your situation a join will be better; the code above is a cursorless iteration, which is overkill for your situation.

Dynamic table in SQL Server

I have a really weird and complex requirement that I need help with. I have a table let's say Tasks that contains all the tasks for a user/system. I need to filter out the tasks per user and show it in UI. But here is the scene, the Tasks table contains a column base_table that stores the table name (real SQL Server table) on which it is based. It also stores the base table id which navigates to a particular record in the base table. Now I need to add some filter in the base table and if it satisfies the task would get retrieved.
I did try to put up a procedure which would hit a select query against base table and also check conditions.
CREATE PROCEDURE gautam_dtTable_test
(#TableName AS nvarchar(max))
AS
BEGIN try
declare #sql nvarchar(max)
declare #ret tinyint
set #ret = 0
set #sql = 'select 1 where exists (Select top 1 Id from ' + #TableName+' where some_condition)';
Exec sp_executesql #sql, N'#var tinyint out', #ret out
return #ret
end try
begin catch
return 0
end catch
I have used the procedure to input table name and hit some conditions and return a flag, 1/0 kind of thing. I also want to use try catch so that if there is any error, it would return false.
That's why I have used the procedure, not function. But seems like we can use this procedure into sql statement. Overall what I have in my mind is
Select *
from tasks
where some_conditions
and procedure/function_to_check(tasks.base_table)
Key issues with my approach
The base_table name could be invalid, so are some columns in it. So, I would love to use a try-catch.
Need to Embed it as sub-query to avoid parallel operations. But it seems tough when your procedure/function have EXEC and sp_executesql defined.
Any kind of help is appreciated. Thanks in advance!
The question as stated is a bit unclear so I am going to make some assumptions here. It looks like you are trying achieve the following:
First it seems you are trying to only return task in your task table where the ‘base_table’ column value references a valid SQL Server table.
Secondly if I understand the post correctly, based on the where clause condition passed to the tasks table you are trying to determine if the same columns exists in your base table.
The first part is certainly doable. However, the second part is not since it would require the query to somehow parse itself to determine what columns are being filtered on.
The following query show how you can retrieve only tasks for which there is a valid corresponding table.
SELECT *
FROM [dbo].[tasks] ts
CROSS APPLY (
SELECT [name]
FROM sys.objects WHERE object_id = OBJECT_ID('[dbo].' + QUOTENAME(ts.base_table)) AND type in (N'U')
) tb
If the field(s) you are trying to filter on is known up front (i.e. you are not trying to parse based of the tasks table) then you can modify the above query to pass the desired columns you want to check as follow:
DECLARE #columnNameToCheck NVARCHAR(50) = 'col2'
SELECT ts.*
FROM [dbo].[tasks] ts
CROSS APPLY (
SELECT [name]
FROM sys.objects WHERE object_id = OBJECT_ID('[dbo].' + QUOTENAME(ts.base_table)) AND type in (N'U')
) tb
CROSS APPLY (
SELECT [name]
FROM sys.columns WHERE object_id = OBJECT_ID('[dbo].' + QUOTENAME(ts.base_table)) AND [name] = #columnName

SQL Script add records with identity FK

I am trying to create an SQL script to insert a new row and use that row's identity column as an FK when inserting into another table.
This is what I use for a one-to-one relationship:
INSERT INTO userTable(name) VALUES(N'admin')
INSERT INTO adminsTable(userId,permissions) SELECT userId,255 FROM userTable WHERE name=N'admin'
But now I also have a one-to-many relationship, and I asked myself whether I can use less SELECT queries than this:
INSERT INTO bonusCodeTypes(name) VALUES(N'1500 pages')
INSERT INTO bonusCodeInstances(codeType,codeNo,isRedeemed) SELECT name,N'123456',0 FROM bonusCodeTypes WHERE name=N'1500 pages'
INSERT INTO bonusCodeInstances(codeType,codeNo,isRedeemed) SELECT name,N'012345',0 FROM bonusCodeTypes WHERE name=N'1500 pages'
I could also use sth like this:
INSERT INTO bonusCodeInstances(codeType,codeNo,isRedeemed)
SELECT name,bonusCode,0 FROM bonusCodeTypes JOIN
(SELECT N'123456' AS bonusCode UNION SELECT N'012345' AS bonusCode)
WHERE name=N'1500 pages'
but this is also a very complicated way of inserting all the codes, I don't know whether it is even faster.
So, is there a possibility to use a variable inside SQL statements? Like
var lastinsertID = INSERT INTO bonusCodeTypes(name) OUTPUT inserted.id VALUES(N'300 pages')
INSERT INTO bonusCodeInstances(codeType,codeNo,isRedeemed) VALUES(lastinsertID,N'123456',0)
OUTPUT can only insert into a table. If you're only inserting a single record, it's much more convenient to use SCOPE_IDENTITY(), which holds the value of the most recently inserted identity value. If you need a range of values, one technique is to OUTPUT all the identity values into a temp table or table variable along with the business keys, and join on that -- but provided the table you are inserting into has an index on those keys (and why shouldn't it) this buys you nothing over simply joining the base table in a transaction, other than lots more I/O.
So, in your example:
INSERT INTO bonusCodeTypes(name) VALUES(N'300 pages');
DECLARE #lastInsertID INT = SCOPE_IDENTITY();
INSERT INTO bonusCodeInstances(codeType,codeNo,isRedeemed) VALUES (#lastInsertID, N'123456',0);
SELECT #lastInsertID AS id; -- if you want to return the value to the client, as OUTPUT implies
Instead of VALUES, you can of course join on a table instead, provided you need the same #lastInsertID value everywhere.
As to your original question, yes, you can also assign variables from statements -- but not with OUTPUT. However, SELECT #x = TOP(1) something FROM table is perfectly OK.

Copy table rows using OUTPUT INTO in SQL Server 2005

I have a table which I need to copy records from back into itself. As part of that, I want to capture the new rows using an OUTPUT clause into a table variable so I can perform other opertions on the rows as well in the same process. I want each row to contain its new key and the key it was copied from. Here's a contrived example:
INSERT
MyTable (myText1, myText2) -- myId is an IDENTITY column
OUTPUT
Inserted.myId,
Inserted.myText1,
Inserted.myText2
INTO
-- How do I get previousId into this table variable AND the newly inserted ID?
#MyTable
SELECT
-- MyTable.myId AS previousId,
MyTable.myText1,
MyTable.myText2
FROM
MyTable
WHERE
...
SQL Server barks if the number of columns on the INSERT doesn't match the number of columns from the SELECT statement. Because of that, I can see how this might work if I added a column to MyTable, but that isn't an option. Previously, this was implemented with a cursor which is causing a performance bottleneck -- I'm purposely trying to avoid that.
How do I copy these records while preserving the copied row's key in a way that will achieve the highest possible performance?
I'm a little unclear as to the context - is this in an AFTER INSERT trigger.
Anyway, I can't see any way to do this in a single call. The OUTPUT clause will only allow you to return rows that you have inserted. What I would recommend is as follows:
DECLARE #MyTable (
myID INT,
previousID INT,
myText1 VARCHAR(20),
myText2 VARCHAR(20)
)
INSERT #MyTable (previousID, myText1, myText2)
SELECT myID, myText1, myText2 FROM inserted
INSERT MyTable (myText1, myText2)
SELECT myText1, myText2 FROM inserted
-- ##IDENTITY now points to the last identity value inserted, so...
UPDATE m SET myID = i.newID
FROM #myTable m, (SELECT ##IDENTITY - ROW_NUMBER() OVER(ORDER BY myID DESC) + 1 AS newID, myID FROM inserted) i
WHERE m.previousID = i.myID
...
Of course, you wouldn't put this into an AFTER INSERT trigger, because it will give you a recursive call, but you could do it in an INSTEAD OF INSERT trigger. I may be wrong on the recursive issue; I've always avoid the recursive call, so I've never actually found out. Using ##IDENTITY and ROW_NUMBER(), however, is a trick I've used several times in the past to do something similar.

Dynamically create temp table based on resultset from SP

I have a SP that calls another SP and the resultset needs to be filtered to only the columns that I am interested in.
DECLARE #myTable TABLE
(
Field1 INT,
Field2 INT,
Field3 INT
)
--If someSP returns say 30 params and I need only 3 params, I don't want to declare all 30 in my temp table #myTable
INSERT INTO #myTable
(Field1, Field2, Field3)
EXEC someSP --Need to selectively filter recordset from SP
#InputParam1 = 'test'
If I cannot do this, I would want to create the temp table DYNAMICALLY based on the resultset from someSP (This way it relieves maintenance issues when someSP is modified to add a new param, I dont need to modify this proc as well
Short answer: no, you can't do that.
You have to pre-declare your temp table with the exact number of columns that will be returned from the stored proc.
The workaround is to use persistent tables. For example, you could have a permanent table in your database called someSPResults. Whenever someSP is changed to have a different number of output columns, change the format of someSPResults as part of the deployment.
Then you can either do this:
insert into dbo.someSPresults
exec someSP
Or inside someSP, you can have the results be inserted directly into the someSPresults table as a normal part of execution. You just have to make sure to identify exactly which records in the someSPresults table came from each execution of someSP, because that stored proc could be fired multiple times simultaneously, thereby dumping a lot of data into someSPresults.
cmsjr stated, "A table variable cannot be the target of a result set from another stored procedure."
I thought that was true too, but then I tested it. This code works in both 2005 and 2008:
CREATE PROCEDURE someSP (#InputParam1 varchar(100)) AS
SELECT LEN(#InputParam1), DATALENGTH(#InputParam1), ##SPID
GO
DECLARE #myTable TABLE (
Field1 INT,
Field2 INT,
Field3 INT
)
INSERT INTO #myTable (Field1, Field2, Field3)
EXEC someSP
#InputParam1 = 'test'
SELECT * FROM #myTable
I knew that would work with #temp tables, but I thought it would not work with #temp tables.
That doesn't answer DotnetDude's question though.
A table variable cannot be the target of a result set from another stored procedure, also you can't perform DDL on table variables after they are declared, they will always have the same definition they were declared with. Temp table is your best bet.
I could think of two options, but I didn't have time to test them: convert the SP into an User Defined Function and use the SELECT * FROM {function} INTO {table}, or use OPENROWSET:
SELECT *
FROM OPENROWSET('SQLOLEDB',
'servername';'username';'password',
'exec dbname.{owner}.yourstoredproc') AS spResult
INTO {tablename}
Both solutions should create the table on the fly, then you can simply select from it.
Based on the comments above, I'd suggest you consider a table valued function.
This can be parameterised and you can do this:
INSERT #foo (col1, col14, col29)
SELECT col1, col14, col29 FROM dbo.ufnTVF (#p1, #p2)
Otherwise, it's OPENROWSET as the "cleanest" (I use this loosely) solution
Or, you modify the resultset of your stored proc to onlky return the columns you want.
This implies dynamic SQL or lots of IF statements. Which in some circumstances will not parse correctly (with SET FMTONLY etc).
You could be trying to code against a 3rd party app or system stored procs (we don't have full details), but it feels messy and wrong. SQL Server 2005 has a huge number of DMVs and catalogue (or catalog depending on which side of the Atlantic you are) views that remove the need for system proc calls.
If you're trying to mimic some aspects of OO design (one proc to do something for everyboy), then I wouldn't. If you need a query that retruns 3 columns of 30, then do so. This will run far better because unused tables and columns will be ignored on in the plan, indeed do not need included.

Resources