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$
Is there a way to clone the table definition from an existing table and recreate as a table variable?
DECLARE #TempTable1 TABLE (ID INT, Description VARCHAR(256))
I need to recreate a set of tables with same number of columns and definitions without repeating the DECLARE TABLE statement.
This process is available on MySQL as below.
CREATE TABLE TempTable1 LIKE TempTableMain;
Is it possible to do this is Microsoft SQL Server?
Please note that the actual scenario contains more that 60 columns in the #TempTable and need to create more than 10 instances from the original table.
I am not talking about data insertion or SELECT ion from another table as below. I need to create the table definition.
DECLARE #TempTable TABLE(ID INT, Description VARCHAR(100))
INSERT INTO #TempTable
VALUES (1, 'Test1'), (1, 'Test1');
SELECT *
INTO #TempTable2
FROM #TempTable1
SELECT * FROM #TempTable2
Create a user defined type with the columns of your table, lets say like that:
CREATE TYPE MyTableType AS TABLE (ID INT, Description VARCHAR(256));
And then declare your table variables using this type:
DECLARE #Table1 MyTableType;
DECLARE #Table2 MyTableType;
DECLARE #Table3 MyTableType;
SQL Server management studio gives you the option to create a sql script to create an already existing table.
Right click your table -> script table as -> CREATE To -> New Query Editor window
This way you dont have to write out the whole query every single time.
You could even create a stored procedure which takes as argument the name of your to be created table and run this from a while loop.
You can perform the following command:
SELECT * INTO #MyTable_tmp FROM MyTable
Then modify your MyTable, and copy your data back in. Other approaches I've seen is to create a new table calling it Mytable_Tmp (Not a temp table), which will be your new table.
Then copy your data doing any migrations you need. Then you will drop the original table and do a rename on Mytable.
When you run SELECT * INTO #MyTable FROM MyTable, SQL Server creates a new temporary table called #MyTable that matches each column and data type from your select clause. In this case we are selecting * so it will match MyTable. This only creates the columns it doesn't copy defaults, constraints indexes or anything else.
If you are using table variables, it means that you don't want to use them in long period of time, as they will be "forgotten" after every script completion.
So, easiest in my opinion is to use such construct:
IF OBJECT_ID('tempdb.dbo.#tmpTable', 'U') IS NOT NULL
DROP TABLE #tmpTable;
SELECT * INTO #tmpTable FROM MyPrimaryTable
It creates temporary table exactly like yours, if you want empty table, you can just use:
SELECT * INTO #tmpTable FROM MyPrimaryTable WHERE 1 = 0
Then, temporary table will have exact same schema as your primary table.
You can apply as many times as you need (create as many temporary tables as you need).
You could use regular tables instead of temporary tables as well.
If you want to re-create table after dropping the existing table then you can use the below query.
/*
Create brands table
*/
-- Old block of code
IF EXISTS (SELECT * FROM sys.objects
WHERE object_id = OBJECT_ID(N'[TOY].[BRANDS]') AND type in (N'U'))
DROP TABLE [TOY].[BRANDS]
GO
-- New block of code
DROP TABLE IF EXISTS [TOY].[BRANDS]
GO
-- Add new table
CREATE TABLE TOY.BRANDS
(
ID INT NOT NULL,
NAME VARCHAR(20) NULL
)
GO
-- Load the table with data
INSERT INTO TOY.BRANDS (ID, NAME) VALUES
(1, 'Ford'),
(2, 'Chevy'),
(3, 'Dodge'),
(4, 'Plymouth'),
(5, 'Oldsmobile'),
(6, 'Lincoln'),
(7, 'Mercury');
GO
I'm beginner and i trying to create trigger to check if table changed then i track the changed and insert it into another table
i created table called history and this is my code
create table history
(
ProjectNo INT ,
UserName NVARCHAR(50),
ModifiedDate date,
Budget_Old int,
Budget_New int,
)
and created this trigger
CREATE TRIGGER t1 on history
on project AFTER UPDATE
AS BEGIN
IF UPDATE(Project.Pnumber,Project.Budget)
INSERT INTO dbo.history (ProjectNo , Username,ModifiedDate,Budget_Old,Budget_New)
SELECT
d.ProjectNo, suser_name() ,GETDATE(),d.Budget,i.budget
FROM
Deleted d , inserted i
where d.projectno = i.projectno
END
i think my if statment is wrong but what i should do to make my query run right
to insert this values in history table ? plz help me and sorry for bad English
Triggers have access to two logical tables that have an identical structure to the table they are defined on which us Project as I assume.
INSERTED, which is the new data to go into the table
DELETED, which is the old data the is in the table
So You can get the old values in this way:
CREATE TRIGGER t1
ON dbo.Project AFTER UPDATE
AS
INSERT INTO dbo.history (ProjectNo , Username)
SELECT d.ProjectNo , d.Username
FROM DELETED d
If you need to just have one record for specific project in the table history, then you would use inner join based on the projectNo and update the history table accordingly.
ITS WORKS BUDDY ENJOY!!!!!!!!!!!
CREATE TRIGGER tRIGGERNAME1
BEFORE UPDATE on history
FOR EACH ROW
BEGIN
INSERT INTO dbo.history (ProjectNo ,Budget_Old,Budget_New)
VALUES(:OLD.ProjectNo,:OLD.Budget_Old,:NEW.Budget_New);
END;
I've never seen this happen before, very odd.
I have a local SQL Server 2012 Express database that I'm developing against. Running a simple suite of tests using the TestDrive plugin and accessing the database with EF v5.
I just ran a test that inserts a record into the database. I had 9 rows in the table going from id 1-9. The next insert and the ID jumped by exactly 10000 !!!!
The Id column goes:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10009
I know failed inserts also increment the ID but I can guarantee that 10,000 didn't fail to insert in the 5 seconds between test runs ...
The table structure is really simple, a bunch of columns and one auto incrementing, identity column of type bigint (long), no SPs, triggers or any other programmatic content.
[Id] [bigint] IDENTITY(1,1) NOT NULL,
Very confusing, has anyone else seen this happening?
This blog post has some additional details. It looks like in 2012, identity is implemented as a sequence. And by default, a sequence has a cache. If the cache is lost you lose the sequence values in the cache.
The proposed solution is to create a sequence with no cache:
CREATE SEQUENCE TEST_Sequence
AS INT
START WITH 1
INCREMENT BY 1
NO CACHE
As far as I can see, the sequence behind an identity column is invisible. You can't change it's properties to disable caching.
To use this with Entity Framework, you could set the primary key's StoredGeneratedPattern to Computed. Then you could generate the identity server-side in an instead of insert trigger:
if exists (select * from sys.sequences where name = 'Sequence1')
drop sequence Sequence1
if exists (select * from sys.tables where name = 'Table1')
drop table Table1
if exists (select * from sys.triggers where name = 'Trigger1')
drop trigger Trigger1
go
create sequence Sequence1
as int
start with 1
increment by 1
no cache
go
create table Table1
(
id int primary key,
col1 varchar(50)
)
go
create trigger Trigger1
on Table1
instead of insert
as
insert Table1
(ID, col1)
select next value for Sequence1
, col1
from inserted
go
insert Table1 (col1) values ('row1');
insert Table1 (col1) values ('row2');
insert Table1 (col1) values ('row3');
select *
from Table1
If you find a better solution, let me know :)
If you will call "Checkpoint" command after each insert query, it will solve your problem.
For more information, please read out Checkpoint in SQL Server
I'm not sure if its standard SQL:
INSERT INTO tblA
(SELECT id, time
FROM tblB
WHERE time > 1000)
What I'm looking for is: what if tblA and tblB are in different DB Servers.
Does PostgreSql gives any utility or has any functionality that will help to use INSERT query with PGresult struct
I mean SELECT id, time FROM tblB ... will return a PGresult* on using PQexec. Is it possible to use this struct in another PQexec to execute an INSERT command.
EDIT:
If not possible then I would go for extracting the values from PQresult* and create a multiple INSERT statement syntax like:
INSERT INTO films (code, title, did, date_prod, kind) VALUES
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');
Is it possible to create a prepared statement out of this!! :(
As Henrik wrote you can use dblink to connect remote database and fetch result. For example:
psql dbtest
CREATE TABLE tblB (id serial, time integer);
INSERT INTO tblB (time) VALUES (5000), (2000);
psql postgres
CREATE TABLE tblA (id serial, time integer);
INSERT INTO tblA
SELECT id, time
FROM dblink('dbname=dbtest', 'SELECT id, time FROM tblB')
AS t(id integer, time integer)
WHERE time > 1000;
TABLE tblA;
id | time
----+------
1 | 5000
2 | 2000
(2 rows)
PostgreSQL has record pseudo-type (only for function's argument or result type), which allows you query data from another (unknown) table.
Edit:
You can make it as prepared statement if you want and it works as well:
PREPARE migrate_data (integer) AS
INSERT INTO tblA
SELECT id, time
FROM dblink('dbname=dbtest', 'SELECT id, time FROM tblB')
AS t(id integer, time integer)
WHERE time > $1;
EXECUTE migrate_data(1000);
-- DEALLOCATE migrate_data;
Edit (yeah, another):
I just saw your revised question (closed as duplicate, or just very similar to this).
If my understanding is correct (postgres has tbla and dbtest has tblb and you want remote insert with local select, not remote select with local insert as above):
psql dbtest
SELECT dblink_exec
(
'dbname=postgres',
'INSERT INTO tbla
SELECT id, time
FROM dblink
(
''dbname=dbtest'',
''SELECT id, time FROM tblb''
)
AS t(id integer, time integer)
WHERE time > 1000;'
);
I don't like that nested dblink, but AFAIK I can't reference to tblB in dblink_exec body. Use LIMIT to specify top 20 rows, but I think you need to sort them using ORDER BY clause first.
If you want insert into specify column:
INSERT INTO table (time)
(SELECT time FROM
dblink('dbname=dbtest', 'SELECT time FROM tblB') AS t(time integer)
WHERE time > 1000
);
This notation (first seen here) looks useful too:
insert into postagem (
resumopostagem,
textopostagem,
dtliberacaopostagem,
idmediaimgpostagem,
idcatolico,
idminisermao,
idtipopostagem
) select
resumominisermao,
textominisermao,
diaminisermao,
idmediaimgminisermao,
idcatolico ,
idminisermao,
1
from
minisermao
You can use dblink to create a view that is resolved in another database. This database may be on another server.
insert into TABLENAMEA (A,B,C,D)
select A::integer,B,C,D from TABLENAMEB
If you are looking for PERFORMANCE, give where condition inside the db link query.
Otherwise it fetch all data from the foreign table and apply the where condition.
INSERT INTO tblA (id,time)
SELECT id, time FROM dblink('dbname=dbname port=5432 host=10.10.90.190 user=postgresuser password=pass123',
'select id, time from tblB where time>'''||1000||'''')
AS t1(id integer, time integer)
I am going to SELECT Databasee_One(10.0.0.10) data from Database_Two (10.0.0.20)
Connect to 10.0.0.20 and create DBLink Extenstion:
CREATE EXTENSION dblink;
Test the connection for Database_One:
SELECT dblink_connect('host=10.0.0.10 user=postgres password=dummy dbname=DB_ONE');
Create foreign data wrapper and server for global authentication:
CREATE FOREIGN DATA WRAPPER postgres VALIDATOR postgresql_fdw_validator;
You can use this server object for cross database queries:
CREATE SERVER dbonepostgres FOREIGN DATA WRAPPER postgres OPTIONS (hostaddr '10.0.0.10', dbname 'DB_ONE');
Mapping of user and server:
CREATE USER MAPPING FOR postgres SERVER dbonepostgres OPTIONS (user 'postgres', password 'dummy');
Test dblink:
SELECT dblink_connect('dbonepostgres');
Import data from 10.0.0.10 into 10.0.0.20
INSERT INTO tableA
SELECT
column1,
,column2,
...
FROM dblink('dbonepostgres', 'SELECT column1, column2, ... from public.tableA')
AS data(column1 DATATYPE, column2 DATATYPE, ...)
;
Here's an alternate solution, without using dblink.
Suppose B represents the source database and A represents the target database:
Then,
Copy table from source DB to target DB:
pg_dump -t <source_table> <source_db> | psql <target_db>
Open psql prompt, connect to target_db, and use a simple insert:
psql
# \c <target_db>;
# INSERT INTO <target_table>(id, x, y) SELECT id, x, y FROM <source_table>;
At the end, delete the copy of source_table that you created in target_table.
# DROP TABLE <source_table>;