when trying to write the output of a stored procedure into a temp table, I get the error message
Msg 208, Level 16, State 0, Line 4
Invalid object name '#tblTemp '.
My query is this:
DECLARE #group_name varchar(250)
SET #group_name = 'somevalue'
INSERT INTO #tblTemp EXEC mySchema.sp_MyStoredProc #group_name OUTPUT
SELECT *
FROM #tblTemp
DROP TABLE #tblTemp
What is wrong here?
Thanks for your help!
To use temp table this way in INSERT INTO you should define this table first.
CREATE TABLE #tblTemp(
ID int,
....
)
In T-SQL a temp table can be created automatically using following command:
select * into #tblTemp
from table
You can use this syntax in your case with stored procedure results using OPENROWSET.
Here is the answer on SO which can help
You need to create the #temp table with e.g. a CREATE TABLE statement before you can INSERT INTO it.
Related
I create a function that returns TEXT, end I'm trying to create a simple query to test this function.
the query while looks like this:
CREATE TEMP TABLE function_test(actuel TEXT,expected TEXT,t_result TEXT);
/* the t_result should be'passed' if function(actuel) = expected */
INSERT INTO
function_test(actuel ,expected);
VALUES
('a','A'),/*function(`a`) return 'A'*/
('b','B'),/*function(`b`) return 'B'*/
('c','C');/*function(`c`) return 'C'*/
IF function(actuel)=expected THEN
INSERT INTO
function_test(t_result) VALUES 'passed' ;
ELSE
INSERT INTO
function_test(t_result) VALUES 'failed';
SELECT * FROM function_test;
DROP TABLE function_test;
Output
It would be nice if I could do it better than this.
thinks.
Not entirely sure I understand what you are asking, but could you not just do either
select MY_FUNCTION('a','A') from dual;
or if you need a row on success and no row on failure
select 'PASS' from dual
where MY_FUNCTION('a','A') = 'YES'
DUAL is a 1 one row table that exists in every Oracle database.
I have a complex type defined as:
create type TP_IdAndVer AS
(
id uuid,
ver integer
);
And I'd like to insert an array of them into a temporary table (so I can join on a composite key).
This is what I have so far but it doesn't work:
DO $$
declare fred TP_IdAndVer[];
BEGIN
drop table if exists tmpTable;
fred := array[ ('034892e4-6137-440c-bb62-b609b60575aa'::uuid, 1), ('034892e4-6137-440c-bb62-b609b60575aa'::uuid, 2) ];
create temporary table temptbl_ids_and_vers(id uuid, ver integer)
on commit drop;
-- I want to insert each row of fred as a row into my temp table. But how??
insert into temptbl_ids_and_vers(id, ver) values (fred);
CREATE TEMPORARY TABLE tmpTable AS
select * from temptbl_ids_and_vers;
END $$;
select *
from tmpTable;
I get this error:
ERROR: INSERT has more target columns than expressions
LINE 1: insert into temptbl_ids_and_vers(id, ver) values (fred)
^
QUERY: insert into temptbl_ids_and_vers(id, ver) values (fred)
CONTEXT: PL/pgSQL function inline_code_block line 15 at SQL statement
********** Error **********
ERROR: INSERT has more target columns than expressions
SQL state: 42601
Context: PL/pgSQL function inline_code_block line 15 at SQL statement
If someone could point me to the right syntax to use it would be a big help. I guess I could loop through each element of the array inserting each row individually, but that feels a bit hacky.
Alternatively, is there a way of passing a table to a stored proc? This would get around what I'm trying to achieve at the moment since I could just a pass a table in without needing to worry about arrays.
Thanks,
Adam.
INSERT INTO temptbl_ids_and_vers(id, ver)
SELECT *
FROM UNNEST(fred) AS q(id, ver)
insert into temptbl_ids_and_vers(id, ver)
select id, ver
from unnest(fred) u
I have two tables. Lets say tblA and tblB.
I need to insert a row in tblA and use the returned id as a value to be inserted as one of the columns in tblB.
I tried finding out this in documentation but could not get it. Well, is it possible to write a statement (intended to be used in prepared) like
INSERT INTO tblB VALUES
(DEFAULT, (INSERT INTO tblA (DEFAULT, 'x') RETURNING id), 'y')
like we do for SELECT?
Or should I do this by creating a Stored Procedure?. I'm not sure if I can create a prepared statement out of a Stored Procedure.
Please advise.
Regards,
Mayank
You'll need to wait for PostgreSQL 9.1 for this:
with
ids as (
insert ...
returning id
)
insert ...
from ids;
In the meanwhile, you need to use plpgsql, a temporary table, or some extra logic in your app...
This is possible with 9.0 and the new DO for anonymous blocks:
do $$
declare
new_id integer;
begin
insert into foo1 (id) values (default) returning id into new_id;
insert into foo2 (id) values (new_id);
end$$;
This can be executed as a single statement. I haven't tried creating a PreparedStatement out of that though.
Edit
Another approach would be to simply do it in two steps, first run the insert into tableA using the returning clause, get the generated value through JDBC, then fire the second insert, something like this:
PreparedStatement stmt_1 = con.prepareStatement("INSERT INTO tblA VALUES (DEFAULT, ?) returning id");
stmt_1.setString(1, "x");
stmt_1.execute(); // important! Do not use executeUpdate()!
ResultSet rs = stmt_1.getResult();
long newId = -1;
if (rs.next()) {
newId = rs.getLong(1);
}
PreparedStatement stmt_2 = con.prepareStatement("INSERT INTO tblB VALUES (default,?,?)");
stmt_2.setLong(1, newId);
stmt_2.setString(2, "y");
stmt_2.executeUpdate();
You can do this in two inserts, using currval() to retrieve the foreign key (provided that key is serial):
create temporary table tb1a (id serial primary key, t text);
create temporary table tb1b (id serial primary key,
tb1a_id int references tb1a(id),
t text);
begin;
insert into tb1a values (DEFAULT, 'x');
insert into tb1b values (DEFAULT, currval('tb1a_id_seq'), 'y');
commit;
The result:
select * from tb1a;
id | t
----+---
3 | x
(1 row)
select * from tb1b;
id | tb1a_id | t
----+---------+---
2 | 3 | y
(1 row)
Using currval in this way is safe whether in or outside of a transaction. From the Postgresql 8.4 documentation:
currval
Return the value most recently
obtained by nextval for this sequence
in the current session. (An error is
reported if nextval has never been
called for this sequence in this
session.) Because this is returning a
session-local value, it gives a
predictable answer whether or not
other sessions have executed nextval
since the current session did.
You may want to use AFTER INSERT trigger for that. Something along the lines of:
create function dostuff() returns trigger as $$
begin
insert into table_b(field_1, field_2) values ('foo', NEW.id);
return new; --values returned by after triggers are ignored, anyway
end;
$$ language 'plpgsql';
create trigger trdostuff after insert on table_name for each row execute procedure dostuff();
after insert is needed because you need to have the id to reference it. Hope this helps.
Edit
A trigger will be called in the same "block" as the command that triggered it, even if not using transactions - in other words, it becomes somewhat part of that command.. Therefore, there is no risk of something changing the referenced id between inserts.
We have decided to move from OIDs in our PostgreSQL 9.0 database and use bytea columns instead. I'm trying to copy the data from one column to the other, but I can't figure out the right query. This is the closest I've gotten to:
update user as thistable set pkcs_as_bytea = (select array_agg(mylargeobject.data) from
(select * from pg_largeobject where loid = thistable.pkcs12_as_oid order by pageno) as mylargeobject) where thistable.pkcs12 is not null
And that gives me the following error message:
ERROR: column "pkcs_as_bytea" is of type bytea but expression is of type bytea[]
What would be the right query then?
Another way which doesn't require a custom function is to use the loread(lo_open(...)) combination, like:
UPDATE user SET pkcs_as_bytea = loread(lo_open(pkcs12_as_oid, 262144), 1000000) WHERE thistable.pkcs12 IS NOT NULL
There is a problem with this code, the loread function requires as the second parameter the maximum number of bytes to read (the 1000000 parameter I used above), so you should use a really big number here if your data is big. Otherwise, the content will be trimmed after this many bytes, and you won't get all the data back into the bytea field.
If you want to convert from OID to a text field, you should also use a conversion function, as in:
UPDATE user SET pkcs_as_text = convert_from(loread(lo_open(pkcs12_as_oid, 262144), 1000000), 'UTF8')
(262144 is a flag for the open mode, 40000 in hexa, which means "open read-only")
Here is a stored procedure that does the magic:
CREATE OR REPLACE FUNCTION merge_oid(val oid)
returns bytea as $$
declare merged bytea;
declare arr bytea;
BEGIN
FOR arr IN SELECT data from pg_largeobject WHERE loid = val ORDER BY pageno LOOP
IF merged IS NULL THEN
merged := arr;
ELSE
merged := merged || arr;
END IF;
END LOOP;
RETURN merged;
END
$$ LANGUAGE plpgsql;
well, i did something like this. I have attachment table and content column with data in oid type. I migrated with four actions:
ALTER TABLE attachment add column content_bytea bytea
UPDATE attachment SET content_bytea = lo_get(content)
ALTER TABLE attachment drop column content
ALTER TABLE attachment rename column content_bytea to content
You need something like array_to_string(anyarray, text) for text arrays, but in this case an array_to_bytea(largeobjectarray) to concat all sections. You have to create this function yourself, or handle this in application logic.
This is what you can do.
--table thistable --
ALTER TABLE thistable add column se_signed_bytea bytea;
UPDATE thistable SET se_signed_bytea = lo_get(pkcs_as_bytea);
ALTER TABLE thistable drop column pkc`enter code here`s_as_bytea;
ALTER TABLE thistable rename column se_signed_bytea to pkcs_as_bytea;
Perhaps a stupid question!
If I call a stored proc from an After Insert trigger (T-SQL) - then how do I get the values of the "just inserted" data?
e.g.
CREATE TRIGGER dbo.MyTrigger
ON dbo.MyTable
AFTER INSERT
AS
BEGIN
EXEC createAuditSproc 'I NEED VALUES HERE!'
I don't have any identity columns to worry about - I just want to use some of the "just inserted" values to pass into my sproc.
Edit: For clarification - I need this to call a sproc and not do a direct insert to the table, since the sproc does more than one thing. I'm working with some legacy tables I can't currently amend to do things 'properly' (time/resource/legacy code), so I have to work with what I have :(
You get to the newly 'changed' data by using the INSERTED and DELETED pseudo-tables:
CREATE TRIGGER dbo.MyTrigger
ON dbo.MyTable
AFTER INSERT
AS
BEGIN
INSERT INTO myTableAudit(ID, Name)
SELECT i.ID, i.Name
FROM inserted i;
END
Given the example tables
create table myTable
(
ID INT identity(1,1),
Name varchar(10)
)
GO
create table myTableAudit
(
ID INT,
Name varchar(10),
TimeChanged datetime default CURRENT_TIMESTAMP
)
GO
Edit : Apologies, I didn't address the bit about calling a Stored Proc. As per marc_s's comment, note that inserted / deleted can contain multiple rows, which complicates matters with a SPROC. Personally, I would leave the trigger inserting directly into the audit table without the encapsulation of a SPROC. However, if you have SQL 2008, you can use table valued parameters, like so:
CREATE TYPE MyTableType AS TABLE
(
ID INT,
Name varchar(10)
);
GO
CREATE PROC dbo.MyAuditProc #MyTableTypeTVP MyTableType READONLY
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO myTableAudit(ID, Name)
SELECT mtt.ID, mtt.Name
FROM #MyTableTypeTVP mtt;
END
GO
And then your trigger would be altered as like so:
ALTER TRIGGER dbo.MyTrigger
ON dbo.MyTable
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #MyTableTypeTVP AS MyTableType;
INSERT INTO #MyTableTypeTVP(ID, Name)
SELECT i.ID, i.Name
FROM inserted i;
EXEC dbo.MyAuditProc #MyTableTypeTVP;
END
you can then test that this works for both a single and multiple inserts
insert into dbo.MyTable values ('single');
insert into dbo.MyTable
select 'double'
union
select 'insert';
However, if you are using SQL 2005 or lower, you would probably need to use a cursor to loop through inserted passing rows to your SPROC, something too horrible to contemplate.
As a side note, if you have SQL 2008, you might look at Change Data Capture
Edit #2 : Since you need to call the proc, and if you are certain that you only insert one row ...
ALTER TRIGGER dbo.MyTrigger
ON dbo.MyTable
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #SomeInt INT;
DECLARE #SomeName VARCHAR(10);
SELECT TOP 1 #SomeInt = i.ID, #SomeName = i.Name
FROM INSERTED i;
EXEC dbo.MyAuditProc #SomeInt, #SomeName;
END;