I am relatively newbie with SQL Server, and I have some experience and practices from Oracle & PostgreSQL which i want to use in the SQL Server.
I need to create function which takes fields values for new row in the table and which also returning autogenerated ID value of the new record.
First of all I am faced with the fact that the functions in SQL Server can not change data in the tables. The second my discovery was that is the procedures in SQL Server can return values through return #result construction.
I investigate output mechanism of the DML queries, but they returns not scalar but table results.
Be patient and let me more clear. There is PostgreSQL function which doing what I want:
Table creation script:
create table foobar
(
foo bigint not null default nextval('s_foobar'::regclass),
bar character varying(16),
constraint pk_foobar primary key (foo)
);
and function script:
create or replace function f_foobar_insert(p_bar character varying)
returns integer as
$body$declare
result integer;
begin
insert into foobar(bar) values (p_bar) returning foo into result;
return result;
end;$body$ language plpgsql;
Is there any possibility to make something like this in SQL Server in the same way?
In SQL Server, the table creation would be:
create table foobar
(
foo bigint not null identity primary key,
bar varchar(16)
);
The following is one way in SQL Server to get the functionality:
insert into foobar(bar) select 'value';
select ##identity;
This is not really the preferred way. You should really use the output clause:
declare #t table (foo bigint);
insert into foobar(bar)
output inserted.foo into #t
select 'value';
select foo from #t;
You can wrap this in a stored procedure if you like, but it doesn't seem necessary, and stored procedures have different semantics from functions.
Related
I've been doing research and trying things out a bunch of different ways, but with no success. I want to create a temporary table and then as I'm doing some searches, fill it up with stuff. I was able to do this with SQL Server by just declaring a table inside the procedure, but with Postgresql I've read I need to create a temporary table specifically.
My strategy started out with just
CREATE TEMP TABLE myTempTable
(
propOne bigint,
propTwo smallint,
createdAtUtc timestamp(6)
);
I even moved it to right after the "BEGIN". Down the file I get this error:
ERROR: "myTempTable" is not a known variable
LINE 77: SELECT * INTO myTempTable from myResult;
Next, I tried to create the temp table when I'm ready to fill it...
WITH some_updated_records AS
(
UPDATE dbTable
SET tablePropertyStatus = 3
WHERE tablePropertyDate < storedProcedurePropertyDate
RETURNING *
)
CREATE TEMP TABLE myTempTable as
(
SELECT *
FROM some_updated_records
);
I still get the same basic error above, but zero errors until it encounters the myTempTable variable.
I'm definitely not a SQL genius (perhaps, eventually, with your help), so there might be some other things I'm doing wrong. My whole task is to convert a SQL Server stored procedure to Postgresql.
What could I being doing wrong to make that temporary table variable un-declared? Is there a special way I need to declare it ahead of time? Am I making a mistake about how to create or declare a temporary table.
Another strategy could be to just keep saving records into a collection of types, forget the "temp table." Is there a way to do this in plpgsql?
UPDATE w/Examples
This version doesn't work. It stops at the create table.
CREATE OR REPLACE PROCEDURE MyTestProcedure(
p_Endpoint Varchar(256),
p_ContentType Varchar(200),
MaxInProcess int = NULL)
LANGUAGE plpgsql
AS $body$
DECLARE
v_UtcNow timestamp(6);
v_ExpiredProcessing timestamp(6);
BEGIN
SELECT CURRENT_TIMESTAMP into v_UtcNow at time zone 'utc';
WITH first_updated AS (UPDATE MyTable
SET Status = 1
WHERE UpdatedAtUtc < v_UtcNow
RETURNING Id, Status, UpdatedAtUtc)
CREATE TEMP TABLE IF NOT EXISTS statustable AS (SELECT Id, Status, UpdatedAtUtc FROM first_updated)
WITH m_result AS (UPDATE MyTable
SET Status = 3,
WHERE ExpirationDateTimeUtc < v_UtcNow
RETURNING Id, Status, UpdatedAtUtc)
INSERT INTO statustable from m_result;
DROP TABLE statustable;
END;
$body$
This errors out at the table creation.
INE 22: CREATE TEMP TABLE statustable as...
The other example would be something similar to creating the table first and then inserting into it. That's probably where I messed up. Working solution will be added in a minute, if someone doesn't add it in first.
You can use a CTE, but put the CTE within the parentheses for the table creation.
CREATE TEMPORARY TABLE myTempTable AS (
WITH cte_updated_records AS (
UPDATE dbTable
SET tablePropertyStatus = 3
WHERE tablePropertyDate < storedProcedurePropertyDate
RETURNING *
)
SELECT * FROM cte_updated_records
);
https://www.postgresql.org/docs/14/plpgsql-statements.html#PLPGSQL-STATEMENTS-ASSIGNMENT1
Please refer the Tip section:
Tip Note that this interpretation of SELECT with INTO is quite
different from PostgreSQL's regular SELECT INTO command, wherein the
INTO target is a newly created table. If you want to create a table
from a SELECT result inside a PL/pgSQL function, use the syntax CREATE
TABLE ... AS SELECT.
based on this then you can do
CREATE TEMP TABLE statustable AS (here is your query clause)
Maybe you can do update later.
Another Point is as per manual, seems you cannot do CREATE Table by using CTE.
Each auxiliary statement in a WITH clause can be a SELECT, INSERT,
UPDATE, or DELETE; and the WITH clause itself is attached to a primary
statement that can also be a SELECT, INSERT, UPDATE, or DELETE.
https://www.postgresql.org/docs/current/queries-with.html
LukStorms's answer is pretty neat. But serval steps maybe more readable?
When you're debugging, things can get a little crazy. What happens often, I find, is I try one good solution, but I don't know how to implement it quite right, so the following works. I think I was forgetting the select in the INSERT INTO's.
CREATE OR REPLACE PROCEDURE MyTestProcedure(
p_Endpoint Varchar(256),
p_ContentType Varchar(200),
MaxInProcess int = NULL)
LANGUAGE plpgsql
AS $body$
DECLARE
v_UtcNow timestamp(6);
v_ExpiredProcessing timestamp(6);
BEGIN
SELECT CURRENT_TIMESTAMP into v_UtcNow at time zone 'utc';
CREATE TEMP TABLE status_table(
Id bigint,
Status smallint,
CreatedAtUtc timestamp(6));
WITH first_updated AS (UPDATE MyTable
SET Status = 1
WHERE UpdatedAtUtc < v_UtcNow
RETURNING Id, Status, UpdatedAtUtc)
INSERT INTO status_table
SELECT Id, Status, UpdatedAtUtc
FROM first_updated;
WITH m_result AS (UPDATE MyTable
SET Status = 3
WHERE ExpirationDateTimeUtc < v_UtcNow
RETURNING Id, Status, UpdatedAtUtc)
INSERT INTO status_table
select Id, Status, UpdatedAtUtc
from m_result;
DROP TABLE status_table;
END;
$body$
I am new to Sql Server and having difficulty to convert the oracle triggers to Sql server.
Can some one help me with that.Here is one of the example:
create or replace
TRIGGER "NEW".TRG_dummy
BEFORE INSERT ON TBL_dummy
FOR EACH ROW
BEGIN
SELECT SEQ_dummy.NEXTVAL
INTO :NEW.dummy_ID
FROM DUAL;
END;
Any help in this will be appreciated.It would be great if I get a method to convert as I have a lot of other objects to be migrated.
Replacing of the trigger doesn't make sense here since you don't even need a trigger for the same functionality in SQL Server. You only need to declare the column as an IDENTITY, e.g.
CREATE TABLE TBL_AFR (
AFR_ID INT IDENTITY NOT NULL, --<< this automatically does the same thing
.. the other columns
);
I've just begun to learn how to write stored procedures and SQL code outside of the basic DML stuff. Something that I've recently come across is table value parameters. I've found a script to make a TVP and it works just fine but there are two things that I don't understand about it. One, when to use them. What's a typical real world scenario when a TVP would be beneficial. Two, how come when I remove the begin and end from the following script does it work the same; what's the difference between having those keywords and not? SQL Server 2008 R2
use [tempdb]
--this is the database table that will be populated
create table SampleTable
(
id int not null identity (1,1)
,SampleString varchar(50)
,SampleInt int null
)
go
--create the table data type
create type dbo.SampleDataType as table
(
SampleString varchar(50)
,SampleInt int
)
go
--the stored procedure takes the SampleDataType as an input parameter
create proc SampleProc
(
--accepts the TVP as the lone parameter
--make sure that the TVP is readonly
#Sample as dbo.SampleDataType readonly
)
as
begin
--we simply insert the values into the db table from the parameter
insert into SampleTable(SampleString,SampleInt)
select SampleString,SampleInt from #Sample
end
go
--this is the sample script to test that the sproc worked
declare #SampleData as dbo.SampleDataType
insert into #SampleData(SampleString,SampleInt) values ('one',1);
insert into #SampleData(SampleString,SampleInt) values ('two',2);
select * from #SampleData
One real world use is to parameterise an in clause.
Where a query has a filter on (x, y, z, ...) you no longer have to resort to one of the methods here such as passing it in as a comma delimited list and then splitting it.
The BEGIN ... END make no difference there. It defines a block. You might use that after an IF statement for example to group multiple statements together into one logical block.
I want a stored procedure to return the primary key of the new record after it gets executed. I think it will be returned by OUT parameter in the procedure. But how to select the newly inserted row ID ? I don't want to use select MAX(row_id) as it is a multi user environment.
Any procedure sample will be appreciated.
My platform is ISeries DB2 V5 R4. Thanks.
Edit
The row id Column is not an identity column. It uses a sequence for the key which gets generated via a trigger before insert on table.
Edit
Here is what I am trying to do
Begin Stored procedure
Insert into Employees;
(row id gets automatically generated by trigger)
Return row id ;
I want to avoid a select in returning row id.
just set the out parameter to the column that contains the PK.
CREATE PROCEDURE DB2TBL.DO_STUFF (IN Param1 INT, IN Param2 CHAR(32),OUT Param3 INT)
/* Param1 is primary key */
LANGUAGE SQL
P1: BEGIN
DECLARE OUTPARAM INT;
/* Do the stored procedure */
SET OUTPARAM = Param1;
--UPDATED---
Hi Popo,
First off could you give more detail on what you mean when you say the rowid is assigned by a trigger?
If you had a real identity column you would use the IDENTITY_VAL_LOCAL() function like this right after the INSERT: SELECT IDENTITY_VAL_LOCAL() INTO myrowid FROM SYSIBM.SYSDUMMY1; I'm not 100% on that syntax because I generally use embedded SQL and it works differently there so you might have to play with it. IBM documentation is at http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/db2/rbafzscaidentity.htm.
However since you are doing something more complicated, I think this alternate method might work. You'll need to re-format your INSERT to be wrapped in a SELECT.
SELECT myrowid
INTO myrowid
FROM FINAL TABLE (
INSERT INTO myfile (myrowid, other_stuff) VALUES (default, 'blah')
)
You'll need to adjust for the proper field names and so on but I think this will do the trick. There's not much documentation but if you want to see it go to http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/db2/rbafzbackup.htm and scroll all the way down to the bottom of the page.
Cheers
CREATE PROCEDURE ASF_InsertNewAuthorRequest
(IN #REQUESTTYPE CHAR(1), IN #UserID VARCHAR(18), IN #DATECREATED TIMESTAMP, IN #REQUESTSTATUS CHAR(1))
LANGUAGE SQL
DYNAMIC RESULT SETS 1
P1: BEGIN
DECLARE cursor1 CURSOR WITH RETURN for
SELECT IDENTITY_VAL_LOCAL FROM SYSIBM.SYSDUMMY1;
INSERT INTO AFS_REQUEST
( REQUESTTYPE, "UserID", DATECREATED, REQUESTSTATUS )
VALUES
( #REQUESTTYPE, #UserID, #DATECREATED, #REQUESTSTATUS );
OPEN cursor1;
END P1
INSERT INTO [User] (columns)
OUTPUT inserted.userId
VALUES (#values)
This will return the newly created userId column value... very simple.
This is very similar to question 653714, but for MySQL instead of SQL Server.
Basically, I have a complicated select that is the basis for several stored procedures. I would like to share the code across the stored procedures, however, I'm not sure how to do this. One way I could do this is by making the shared select a stored procedure and then calling that stored procedure from the other ones. I can't figure out how to work with the result set of the nested stored procedure. If I could put them in a temp table I could use the results effectively, but I can't figure out how to get them in a temp table. For example, this does not work:
CREATE TEMPORARY TABLE tmp EXEC nested_sp();
The problem is, Stored Procedures don't really return output directly. They can execute select statements inside the script, but have no return value.
MySQL calls stored procedures via CALL StoredProcedureName(); And you cannot direct that output to anything, as they don't return anything (unlike a function).
MySQL Call Command
You cannot "SELECT INTO" with stored procedures.
Create the temporary table first and have your stored procedure to store the query result into the created temporary table using normal "INSERT INTO". The temporary table is visible as long as you drop it or until the connection is closed.
i know this is coming really late but since it took me ages to find a real solution i might as well share. I worked on an example that is below.
the tables created are:
CREATE TABLE BOOK(
B_ID INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(B_ID),
TITLE VARCHAR(100),
DESCRIPTION VARCHAR(30),
PRICE DOUBLE);
CREATE TABLE BOOK_COMMENT(
PRIMARY KEY(B_C_ID),
B_C_ID INT NOT NULL AUTO_INCREMENT,
REMARK VARCHAR(120),
B_ID INT,
FOREIGN KEY(B_ID) REFERENCES BOOK(B_ID));
CREATE TABLE AUTHOR(
A_ID INT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(A_ID),
A_NAME CHAR(15),
B_ID INT,
FOREIGN KEY(B_ID) REFERENCES BOOK(B_ID));
DELIMITER
CREATE PROCEDURE BOOK_IMPORTANT( _PRICE DOUBLE, _B_ID INT, A_NAME CHAR(15), _BD_ID INT)
BEGIN
INSERT INTO BOOK(PRICE)
VALUES(_PRICE);
SET _B_ID=LAST_INSERT_ID();
INSERT INTO BOOK_COMMENT(B_ID)
VALUES(_B_ID);
SET _BD_ID=LAST_INSERT_ID();
INSERT INTO AUTHOR(A_NAME,B_ID)
VALUES(A_NAME,_BD_ID);
END
then use the following to insert the values.
CALL BOOK_IMPORTANT('0.79',LAST_INSERT_ID(),'',LAST_INSERT_ID());
LAST_INSERT_ID() takes the last auto increment of the table and inserts it into the referencing column of the child table.
In the procedure parameters _B_ID and _BD_ID represent the B_ID since I need B_ID as a foreign key in both tables.
Sorry for the excess wording. All the other guys expect you to automatically know how to do it. Hope it helps
My first reaction was "That sounds like a view to me". Doesn't that abstract it enough so you can just add the variability into an SP per case?
Anything that adds a temp table that wouldn't otherwise be there is a very likely antipattern.
Maybe it's a closed topic, but I would like to offer a solution based on the properties of MySQL temporary tables. First, the way to create the temporary table would not be to call the stored procedure "CREATE TEMPORARY TABLE tmp EXEC nested_sp ();". The query is to the temporary table of "infrastructure", (to name it somehow).
To achieve the desired result, it is necessary to create 2 stored procedures, the first stored procedure processes the data and fills the temporary "infrastructure" table, the second stored procedure, reads this table and continues with the process and finally "DROP" the "infrastructure" table
This is the first stored procedure:
CREATE DEFINER = 'root'#'localhost'
PROCEDURE cajareal.priv_test()
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS tmp(
column1 TEXT
, column2 TEXT
, column3 TEXT
);
INSERT INTO tmp(column1, column2 , column3) VALUES(CURDATE(), CURRENT_DATE(), CURRENT_TIMESTAMP());
END
This is the second stored procedure:
CREATE DEFINER = 'root'#'localhost'
PROCEDURE cajareal.priv_caller()
BEGIN
CALL priv_test;
-- Read data of "infrastructure" table
SELECT * FROM tmp;
-- Do the business logic
-- Delete the "infrastructure" table
DROP TABLE tmp;
END
I use this technique to analyze a string and convert it to the table