I have a table and a stored procedure. I use the stored procedure to update the table. There are some cursors in the stored procedure and the SP is updating the table. I want to get the rows updated by the stored procedure. I don't want to number of updated rows, I want just updated rows.
I created a temporary table to insert with updated rows but can't get the updated rows. How can I get?
I am using SQL Server.
If your RDBMS supports it, you can use update returning like this:
sql> update your_table
set your_field = 'my new value'
where other_field = 'your condition'
returning *; -- this returning will return a result set with the modified rows
-- you could also specify a list of columns here if you don't want
-- all fields returned
Using returning clause should work with PostgreSQL, Oracle, and others.
If you are using SQLServer (as you've just stated on your question update), you can use output:
sql> update your_table
set your_field = 'my new value'
output your_list_of_fields -- this is a comma separated list of the
-- columns you want to return
where other_field = 'your condition';
You could use the INSERTED and DELETED virtual or "psuedo" tables which are created for this purpose. In UPDATE statements the virtual tables are accessible using the OUTPUT clause. Here's an example
drop table if exists #t;
go
create table #t(col_x char(1));
insert #t values('a');
update #t
set col_x='b'
output inserted.col_x as new_val,
deleted.col_x as old_val;
new_val old_val
b a
Related
In a BQ stored procedure, I'm doing:
DECLARE varRow_count INT64;
CREATE TABLE `<my_table_name>`
AS
SELECT
<my SQL>
;
SET varRow_count = ##row_count;
But varRow_count ends up being NULL. The SQL definitely populates the table with rows so there should be a row count returned IMHO.
Is this because ##row_count only works with classic INSERT or SELECT or DELETE DML whereas the above is classed as DDL?
Thanks.
I have a database that needs from time to time an update.
It may also happens that there are new data while the update runs.
In MySQL there is a option
INSERT INTO IGNORE
I can't find something like this in T-SQL.
No Problem to update ID 1-4 but then there is a new record for ID 5.
The UPDATE query don't work here.
And when I try to INSERT all data again I get a DUPLICATE KEY error.
Additional Infos:
I've forgotten to say that my data come from external sources. I call an API to get data from it. From there I have to insert these data into my database.
I have to admit that I don't understand MERGE. So my solution for now is to use TRUNCATE first and then insert all data again.
Not the best solution but MERGE works, so far I understand it, with two tables. But I have only one table. And to create a table temporarly to use MERGE and later drop that table is in my eyes a bit to much for my little table with 200 records in it.
You can use MERGE keyword. Basically, you need to specify the column(s) on which to join the source of data with target table, and depending on whether it is matching (existing record) or not matching (new record), you run an UPDATE or INSERT.
Reference: http://msdn.microsoft.com/en-us/library/bb510625.aspx
Is a stored procedure an option?
CREATE PROCEDURE dbo.Testing (#ID int, #Field1 varchar(20))
AS
BEGIN
UPDATE tblTesting
SET Field1 = #Field1
WHERE ID = #ID
IF ##ROWCOUNT = 0
INSERT INTO tblTesting (ID, Field1) SELECT #ID, #Field1
END
For testing purposes I'm wondering if the following two selects could ever be combined:
begin transaction
select x
from example_table
update example_table
set x = 'new value'
select x
from example_table
rollback transaction
Essentially, I'd like to see a result output like:
Column Name, Updated Column Name
If I understand you correctly you can do in using OUTPUT clause (SQL Server 2005 +)
use tempdb
go
create table #tbl (i int)
insert into #tbl values (10),(20),(30),(40)
update #tbl
set i=i+1
output deleted.i i_old,inserted.i i_updated
drop table #tbl
From Books OnLine
OUTPUT Clause returns information from, or expressions based on, each
row affected by an INSERT, UPDATE, DELETE, or MERGE statement.
...
DELETED is a column prefix that specifies the value deleted by the
update or delete operation. Columns prefixed with DELETED reflect the
value before the UPDATE, DELETE, or MERGE statement is completed.
...
INSERTED is a column prefix that specifies the value added by the
insert or update operation. Columns prefixed with INSERTED reflect the
value after the UPDATE, INSERT, or MERGE statement is completed but
before triggers are executed.
EDITED
begin transaction
update example_table
set x = 'new value'
output deleted.x old_value, inserted.x new_value
rollback transaction
Here's what I'm trying to do:
1) Insert into a temp table some values from an original table
INSERT INTO temp_table SELECT id FROM original WHERE status='t'
2) Update the original table
UPDATE original SET valid='t' WHERE status='t'
3) Select based on a join between the two tables
SELECT * FROM original WHERE temp_table.id = original.id
Is there a way to combine steps 1 and 2?
You can combine the steps by doing the update in PL/SQL and using the RETURNING clause to get the updated ids into a PL/SQL table.
EDIT:
If you still need to do the final query, you can still use this method to insert into the temp_table; although depending on what that last query is for, there may be other ways of achieving what you want. To illustrate:
DECLARE
id_table_t IS TABLE OF original.id%TYPE INDEX BY PLS_INTEGER;
id_table id_table_t;
BEGIN
UPDATE original SET valid='t' WHERE status='t'
RETURNING id INTO id_table;
FORALL i IN 1..id_table.COUNT
INSERT INTO temp_table
VALUES (id_table(i));
END;
/
SELECT * FROM original WHERE temp_table.id = original.id;
No, DML statements can not be mixed.
There's a MERGE statement, but it's only for operations on a single table.
Maybe create a TRIGGER wich fires after inserting into a temp_table and updates the original
Create a cursor holding the values from insert and then loop through the cursor updating the table. No need to create temp table in the first place.
You can combine steps 1 and 2 using a MERGE statement and DML error logging. Select twice as many rows, update half of them, and force the other half to fail and then be inserted into an error log that you can use as your temporary table.
The solution below assumes that you have a primary key constraint on ID, but there are other ways you could force a failure.
Although I think this is pretty cool, I would recommend you not use it. It looks very weird, has some strange issues (the inserts into TEMP_TABLE are auto-committed), and is probably very slow.
--Create ORIGINAL table for testing.
--Primary key will be intentionally violated later.
create table original (id number, status varchar2(10), valid varchar2(10)
,primary key (id));
--Create TEMP_TABLE as error log. There will be some extra columns generated.
begin
dbms_errlog.create_error_log(dml_table_name => 'ORIGINAL'
,err_log_table_name => 'TEMP_TABLE');
end;
/
--Test data
insert into original values(1, 't', null);
insert into original values(2, 't', null);
insert into original values(3, 's', null);
commit;
--Update rows in ORIGINAL and also insert those updated rows to TEMP_TABLE.
merge into original original1
using
(
--Duplicate the rows. Only choose rows with the relevant status.
select id, status, valid, rownumber
from original
cross join
(select 1 rownumber from dual union all select 2 rownumber from dual)
where status = 't'
) original2
on (original1.id = original2.id and original2.rownumber = 1)
--Only math half the rows, those with rownumber = 1.
when matched then update set valid = 't'
--The other half will be inserted. Inserting ID causes a PK error and will
--insert the data into the error table, TEMP_TABLE.
when not matched then insert(original1.id, original1.status, original1.valid)
values(original2.id, original2.status, original2.valid)
log errors into temp_table reject limit 999999999;
--Expected: ORIGINAL rows 1 and 2 have VALID = 't'.
--TEMP_TABLE has the two original values for ID 1 and 2.
select * from original;
select * from temp_table;
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.