Insert deleted data, plus some hard-coded values, with output statement - sql

I want to move rows from one table to another, and delete from [foo] output deleted.[col] into [bar] (col) looks like a good option.
But the columns aren't identical. So I want to insert some hard-coded values (and ideally programmatically-determined values) into the destination table.
I set up a couple tables to demonstrate.
create table delete_output_test (
thing1 int not null,
thing2 varchar(50),
thing3 varchar(50)
)
create table delete_output_test2 (
thing1 int not null,
thing2 varchar(50),
thing3 varchar(50),
thing4 int
)
insert into delete_output_test values (0, 'hello', 'world'),
(1, 'it''s', 'me'),
(2, 'i', 'was'),
(3, 'wondering', 'if')
Now moving from one table to another works fine if I'm not too needy...
delete from delete_output_test2
output deleted.thing1,
deleted.thing2,
deleted.thing3
into delete_output_test
(thing1,
thing2,
thing3)
But what if I want to populate that last column?
delete from delete_output_test2
output deleted.thing1,
deleted.thing2,
deleted.thing3
into delete_output_test
(thing1,
thing2,
thing3,
4)
Incorrect syntax near '4'. Expecting '.', ID, PSEUDOCOL, or QUOTED_ID.
I'm fairly new to SQL, so I'm not even sure what those things are.
So why can't I hard-code a value to insert? Or even replace the 4 with some select statement if I want to get clever?

Well, delete_output_test doesn't have a column named 4 or thing4 but delete_output_test2 does. So you can do this:
delete from delete_output_test
output deleted.thing1,
deleted.thing2,
deleted.thing3,
4
into delete_output_test2
(thing1,
thing2,
thing3,
thing4);
select * from delete_output_test2;
rextester demo: http://rextester.com/CVZOB61339
returns:
+--------+-----------+--------+--------+
| thing1 | thing2 | thing3 | thing4 |
+--------+-----------+--------+--------+
| 0 | hello | world | 4 |
| 1 | it's | me | 4 |
| 2 | i | was | 4 |
| 3 | wondering | if | 4 |
+--------+-----------+--------+--------+

The requirement is a little curious, but I think you can do it using a CTE or subquery:
with todelete as (
select dot.*, 4 as col4
from delete_output_test
)
delete from todelete
output deleted.thing1, deleted.thing2, deleted.thing3, deleted.col4
into delete_output_test2(thing1, thing2, thing3, col4);
You need to be sure that delete_output_test has space for the additional column.

Related

postgres insert data from an other table inside array type columns

I have tow table on Postgres 11 like so, with some ARRAY types columns.
CREATE TABLE test (
id INT UNIQUE,
category TEXT NOT NULL,
quantitie NUMERIC,
quantities INT[],
dates INT[]
);
INSERT INTO test (id, category, quantitie, quantities, dates) VALUES (1, 'cat1', 33, ARRAY[66], ARRAY[123678]);
INSERT INTO test (id, category, quantitie, quantities, dates) VALUES (2, 'cat2', 99, ARRAY[22], ARRAY[879889]);
CREATE TABLE test2 (
idweb INT UNIQUE,
quantities INT[],
dates INT[]
);
INSERT INTO test2 (idweb, quantities, dates) VALUES (1, ARRAY[34], ARRAY[8776]);
INSERT INTO test2 (idweb, quantities, dates) VALUES (3, ARRAY[67], ARRAY[5443]);
I'm trying to update data from table test2 to table test only on rows with same id. inside ARRAY of table test and keeping originals values.
I use INSERT on conflict,
how to update only 2 columns quantities and dates.
running the sql under i've got also an error that i don't understand the origin.
Schema Error: error: column "quantitie" is of type numeric but expression is of type integer[]
INSERT INTO test (SELECT * FROM test2 WHERE idweb IN (SELECT id FROM test))
ON CONFLICT (id)
DO UPDATE
SET
quantities = array_cat(EXCLUDED.quantities, test.quantities),
dates = array_cat(EXCLUDED.dates, test.dates);
https://www.db-fiddle.com/f/rs8BpjDUCciyZVwu5efNJE/0
is there a better way to update table test from table test2, or where i'm missing the sql?
update to show result needed on table test:
**Schema (PostgreSQL v11)**
| id | quantitie | quantities | dates | category |
| --- | --------- | ---------- | ----------- | --------- |
| 2 | 99 | 22 | 879889 | cat2 |
| 1 | 33 | 34,66 | 8776,123678 | cat1 |
Basically, your query fails because the structures of the tables do not match - so you cannot insert into test select * from test2.
You could work around this by adding "fake" columns to the select list, like so:
insert into test
select idweb, 'foo', 0, quantities, dates from test2 where idweb in (select id from test)
on conflict (id)
do update set
quantities = array_cat(excluded.quantities, test.quantities),
dates = array_cat(excluded.dates, test.dates);
But this looks much more convoluted than needed. Essentially, you want an update statement, so I would just recommend:
update test
set
dates = test2.dates || test.dates,
quantities = test2.quantities || test.quantities
from test2
where test.id = test2.idweb
Note that this ues || concatenation operator instead of array_cat() - it is shorter to write.
Demo on DB Fiddle:
id | category | quantitie | quantities | dates
-: | :------- | --------: | :--------- | :------------
2 | cat2 | 99 | {22} | {879889}
1 | cat1 | 33 | {34,66} | {8776,123678}

Can I count the occurences for postgres array field?

I have a table postgres that uses the array type of data, it allows some magic making it possible to avoid having more tables, but the non-standard nature of this makes it more difficult to operate with for a beginner.
I would like to get some summary data out of it.
Sample content:
CREATE TABLE public.cts (
id serial NOT NULL,
day timestamp NULL,
ct varchar[] NULL,
CONSTRAINT ctrlcts_pkey PRIMARY KEY (id)
);
INSERT INTO public.cts
(id, day, ct)
VALUES(29, '2015-01-24 00:00:00.000', '{ct286,ct281}');
INSERT INTO public.cts
(id, day, ct)
VALUES(30, '2015-01-25 00:00:00.000', '{ct286,ct281}');
INSERT INTO public.cts
(id, day, ct)
VALUES(31, '2015-01-26 00:00:00.000', '{ct286,ct277,ct281}');
I would like to get the totals per array member occurence totalized, with an output like this for example:
name | value
ct286 | 3
ct281 | 3
ct277 | 1
Use Postgres function array unnest():
SELECT name, COUNT(*) cnt
FROM cts, unnest(ct) as u(name)
GROUP BY name
Demo on DB Fiddle:
| name | cnt |
| ----- | --- |
| ct277 | 1 |
| ct281 | 3 |
| ct286 | 3 |

How to ORDER BY Alphanumeric values in SQL specific columns

create table Employee(id int, Registration_no varchar(50),Name varchar(50))
insert into #Employee values(1,'DLW/TTC/19/3','RAMESH')
insert into #Employee values(2,'DLW/TTC/19/2','RAJEEV')
insert into #Employee values(3,'DLW/TTC/19/1','RUPAK')
insert into #Employee values(4,'DLW/TTC/19/4','RAMLAAL')
insert into #Employee values(5,'DLW/TTC/19/8','RITESH')
insert into #Employee values(6,'DLW/TTC/19/6','HRITIK')
insert into #Employee values(7,'DLW/TTC/19/9','ROSHAN')
insert into #Employee values(8,'DLW/TTC/19/7','RUPALI')
insert into #Employee values(9,'DLW/TTC/19/5','SHRISTI')
insert into #Employee values(10,'DLW/TTC/19/10','ROSHNI')
select * from Employee
Hello I have the table given above.
Actually am facing problem while am trying to order this table's column (Registration_no)
So kindly help me to ORDER it according to its (Registration_no) column
Its not matter that how the other columns are arranged. I just want my Registration_no column to be arranged in the specific order like this
Registration_no
DLW/TTC/19/1
DLW/TTC/19/2
DLW/TTC/19/3
DLW/TTC/19/4
DLW/TTC/19/5
DLW/TTC/19/6
DLW/TTC/19/7
DLW/TTC/19/8
DLW/TTC/19/9
DLW/TTC/19/10
This will sort by the digits to the right of the last / in the Registration_No string. I'm only including the SortColumn in the result set so you can see the values. You can omit it from your query.
SELECT
e.*,
CAST(RIGHT(e.Registration_no,CHARINDEX('/',REVERSE(e.Registration_no))-1) AS INTEGER) AS SortColumn
FROM #Employee AS e
ORDER BY
CAST(RIGHT(e.Registration_no,CHARINDEX('/',REVERSE(e.Registration_no))-1) AS INTEGER)
Results:
+----+-----------------+---------+------------+
| id | Registration_no | Name | SortColumn |
+----+-----------------+---------+------------+
| 3 | DLW/TTC/19/1 | RUPAK | 1 |
| 2 | DLW/TTC/19/2 | RAJEEV | 2 |
| 1 | DLW/TTC/19/3 | RAMESH | 3 |
| 4 | DLW/TTC/19/4 | RAMLAAL | 4 |
| 9 | DLW/TTC/19/5 | SHRISTI | 5 |
| 6 | DLW/TTC/19/6 | HRITIK | 6 |
| 8 | DLW/TTC/19/7 | RUPALI | 7 |
| 5 | DLW/TTC/19/8 | RITESH | 8 |
| 7 | DLW/TTC/19/9 | ROSHAN | 9 |
| 10 | DLW/TTC/19/10 | ROSHNI | 10 |
+----+-----------------+---------+------------+
The SortColumn functions first REVERSE the string, then use CHARINDEX to find the position from the end of the string of the last occurrence of /, then take that number -1 from the right side of the original column (-1 to exclude the / itself).
You can use reverse function together with charindex function
SELECT e.*, cast( reverse(substring(reverse(Registration_no),1,
charindex('/',reverse(Registration_no),1) -1 ) ) as int ) as nr
FROM employee e
ORDER BY nr;
Demo
The main principle is extracting the pieces and converting them to a numerical value, such as integer, in the tail part of the string values. It's easier to operate from the beginning with substring function to this extraction provided reverse function is used to make read the string reversely. And in this case we need to determine the position of the first delimiter(/) by contribution of charindex function. All those functions exist since the version 2008.
If the pattern at the end of Registration_no is always like /X or /XX then:
select * from Employee
order by
case left(right(Registration_no, 2), 1)
when '/' then
left(Registration_no, len(Registration_no) - 1) + '0' + right(Registration_no, 1)
else Registration_no
end
See the demo.
Although I dont like the way the number is extracted, this is how it can be done
select cast(substring(registration_no, charindex('/', registration_no, len(registration_no) -3) + 1, 3) as int),
* from Employee
order by 1
I would suggest you order it on the frontend using regex assuming that you are wanting to order it for display purpose.
How about this.
select *
from Employee
order by LEFT(Registration_no,PATINDEX('%[0-9]%',Registration_no)-1)-- alpha sort
, CONVERT(INT,REPLACE(SUBSTRING(Registration_no,PATINDEX('%[0-9]%',Registration_no),PATINDEX('%[0-9]%',Registration_no)),'/', '')) -- number sort
The first thing you'll notice as you try to order by the Registration_no is that it will be ordered alphabetically due to the nature of the string contents of the column of type varchar. So the correct way to do it is to convert the last two parts of the reg no into numbers and use them in an order by clause. (in this case we can use only the last part since the 2nd last is always 19)
Doing a bit of search I found this function 'Parsename' which has a usage in replication scenarios as it splits the SQL object schema name into its forming parts, so we can use it here (as long as the reg no parts doesn't exceed 4 parts "the SQL objects maximum parts")
So this will work in that case in SQL Server (T-SQL):
SELECT *
FROM Employee
order by cast(Parsename(replace(Registration_no,'/','.'),1) as int)
More info here
Thanks
Try this query.
create table #Employee(id int, Registration_no varchar(50),Name varchar(50))
insert into #Employee values(1,'DLW/TTC/19/3','RAMESH')
insert into #Employee values(2,'DLW/TTC/19/2','RAJEEV')
insert into #Employee values(3,'DLW/TTC/19/1','RUPAK')
insert into #Employee values(4,'DLW/TTC/19/4','RAMLAAL')
insert into #Employee values(5,'DLW/TTC/19/8','RITESH')
insert into #Employee values(6,'DLW/TTC/19/6','HRITIK')
insert into #Employee values(7,'DLW/TTC/19/9','ROSHAN')
insert into #Employee values(8,'DLW/TTC/19/7','RUPALI')
insert into #Employee values(9,'DLW/TTC/19/5','SHRISTI')
insert into #Employee values(10,'DLW/TTC/19/10','ROSHNI')
select * from #Employee
order by Registration_no
id Registration_no Name
----------- -------------------------------------------------- --------------------------------------------------
3 DLW/TTC/19/1 RUPAK
10 DLW/TTC/19/10 ROSHNI
2 DLW/TTC/19/2 RAJEEV
1 DLW/TTC/19/3 RAMESH
4 DLW/TTC/19/4 RAMLAAL
9 DLW/TTC/19/5 SHRISTI
6 DLW/TTC/19/6 HRITIK
8 DLW/TTC/19/7 RUPALI
5 DLW/TTC/19/8 RITESH
7 DLW/TTC/19/9 ROSHAN

Get rows where value is not a substring in another row

I'm writing recursive sql against a table that contains circular references.
No problem! I read that you can build a unique path to prevent infinite loops. Now I need to filter the list down to only the last record in the chain. I must be doing something wrong though. -edit I'm adding more records to this sample to make it more clear why just selecting the longest record doesn't work.
This is an example table:
create table strings (id int, string varchar(200));
insert into strings values (1, '1');
insert into strings values (2, '1,2');
insert into strings values (3, '1,2,3');
insert into strings values (4, '1,2,3,4');
insert into strings values (5, '5');
And my query:
select * from strings str1 where not exists
(
select * from strings str2
where str2.id <> str1.id
and str1.string || '%' like str2.string
)
I'd expect to only get the last records
| id | string |
|----|---------|
| 4 | 1,2,3,4 |
| 5 | 5 |
Instead I get them all
| id | string |
|----|---------|
| 1 | 1 |
| 2 | 1,2 |
| 3 | 1,2,3 |
| 4 | 1,2,3,4 |
| 5 | 5 |
Link to sql fiddle: http://sqlfiddle.com/#!15/7a974/1
My problem was all around the 'LIKE' comparison.
select * from strings str1
where not exists
(
select
*
from
strings str2
where
str2.id <> str1.id
and str2.string like str1.string || '%'
)

Postgresql Sequence vs Serial

I was wondering when it is better to choose sequence, and when it is better
to use serial.
What I want is returning last value after insert using
SELECT LASTVAL();
I read this question
PostgreSQL Autoincrement
I never use serial before.
Check out a nice answer about Sequence vs. Serial.
Sequence will just create sequence of unique numbers. It's not a datatype. It is a sequence. For example:
create sequence testing1;
select nextval('testing1'); -- 1
select nextval('testing1'); -- 2
You can use the same sequence in multiple places like this:
create sequence testing1;
create table table1(id int not null default nextval('testing1'), firstname varchar(20));
create table table2(id int not null default nextval('testing1'), firstname varchar(20));
insert into table1 (firstname) values ('tom'), ('henry');
insert into table2 (firstname) values ('tom'), ('henry');
select * from table1;
| id | firstname |
|----|-----------|
| 1 | tom |
| 2 | henry |
select * from table2;
| id | firstname |
|----|-----------|
| 3 | tom |
| 4 | henry |
Serial is a pseudo datatype. It will create a sequence object. Let's take a look at a straight-forward table (similar to the one you will see in the link).
create table test(field1 serial);
This will cause a sequence to be created along with the table. The sequence name's nomenclature is <tablename>_<fieldname>_seq. The above one is the equivalent of:
create sequence test_field1_seq;
create table test(field1 int not null default nextval('test_field1_seq'));
Also see: http://www.postgresql.org/docs/9.3/static/datatype-numeric.html
You can reuse the sequence that is auto-created by serial datatype, or you may choose to just use one serial/sequence per table.
create table table3(id serial, firstname varchar(20));
create table table4(id int not null default nextval('table3_id_seq'), firstname varchar(20));
(The risk here is that if table3 is dropped and we continue using table3's sequence, we will get an error)
create table table5(id serial, firstname varchar(20));
insert into table3 (firstname) values ('tom'), ('henry');
insert into table4 (firstname) values ('tom'), ('henry');
insert into table5 (firstname) values ('tom'), ('henry');
select * from table3;
| id | firstname |
|----|-----------|
| 1 | tom |
| 2 | henry |
select * from table4; -- this uses sequence created in table3
| id | firstname |
|----|-----------|
| 3 | tom |
| 4 | henry |
select * from table5;
| id | firstname |
|----|-----------|
| 1 | tom |
| 2 | henry |
Feel free to try out an example: http://sqlfiddle.com/#!15/074ac/1
2021 answer using identity
I was wondering when it is better to choose sequence, and when it is better to use serial.
Not an answer to the whole question (only the part quoted above), still I guess it could help further readers. You should not use sequence nor serial, you should rather prefer identity columns:
create table apps (
id integer primary key generated always as identity
);
See this detailed answer: https://stackoverflow.com/a/55300741/978690 (and also https://wiki.postgresql.org/wiki/Don%27t_Do_This#Don.27t_use_serial)