How to run multiple insert statements in a transaction in postgresql? - sql

BEGIN;
DECLARE o_id int;
DECLARE p_id int;
INSERT INTO properties (address, created_at, updated_at)
VALUES ('100 street, city, country, 1b1 b1b', current_timestamp, current_timestamp);
INSERT INTO ownerships (share, created_at, updated_at)
VALUES (1, current_timestamp, current_timestamp)
RETURNING id INTO o_id;
INSERT INTO profiles (first_name, last_name, created_at, updated_at)
values ('j', 'p', current_timestamp, current_timestamp)
RETURNING id INTO p_id;
INSERT INTO profile_ownerships (profile_id, ownership_id, created_at, updated_at)
VALUES (o_id, p_id, current_timestamp, current_timestamp);
COMMIT;
I'm trying to run these insert rows into properties, profiles, and ownerships. Then return the profile and ownership id's for the joining table, profile_ownerships. The above sql is returning an error around 'returning id into ownership_id;' and I'm not sure why.

This turned out to be what I was looking for. Still not sure why it needed to be wrapped in a DO .. END block, though. So if someone has an answer to that, I'd appreciate it.
DO $$
DECLARE
o_id INTEGER;
p_id INTEGER;
BEGIN
INSERT INTO properties (address, created_at, updated_at)
VALUES ('test 7', current_timestamp, current_timestamp);
INSERT INTO ownerships (shares, created_at, updated_at)
VALUES (4, current_timestamp, current_timestamp)
RETURNING id INTO o_id;
INSERT INTO profiles (first_name, last_name, created_at, updated_at)
VALUES ('p', 'j', current_timestamp, current_timestamp)
RETURNING id INTO p_id;
INSERT INTO profile_ownerships (profile_id, ownership_id, created_at, updated_at)
VALUES (p_id, o_id, current_timestamp, current_timestamp);
END $$;

Related

delete rows so that only one row exist for one first name

I created table.
CREATE TABLE test_tab(
ID INT,
FIRSTNAME VARCHAR(40),
TS TIMESTAMP)
And insert values into it.
INSERT INTO test_tab (ID, FIRSTNAME, TS) VALUES (1, 'Jhon', '2018-06-05 00:11:56');
INSERT INTO test_tab (ID, FIRSTNAME, TS) VALUES (2, 'Jhon', '2018-06-15 00:14:56');
INSERT INTO test_tab (ID, FIRSTNAME, TS) VALUES (3, 'Jhon', '2018-06-19 00:10:56');
INSERT INTO test_tab (ID, FIRSTNAME, TS) VALUES (4, 'Mike', '2018-06-05 00:10:56');
INSERT INTO test_tab (ID, FIRSTNAME, TS) VALUES (5, 'Mike', '2018-06-15 00:10:56');
INSERT INTO test_tab (ID, FIRSTNAME, TS) VALUES (6, 'Mike', '2018-06-20 00:10:56');
INSERT INTO test_tab (ID, FIRSTNAME, TS) VALUES (7, 'Lis', '2018-06-05 00:13:56');
INSERT INTO test_tab (ID, FIRSTNAME, TS) VALUES (8, 'Lis', '2018-06-15 00:17:56');
INSERT INTO test_tab (ID, FIRSTNAME, TS) VALUES (9, 'Lis', '2018-06-21 00:10:56');
I need to delete rows so that only one row exist for one first name, leave row with maximum TS.
It is the example of my request.
How can I delete it?
SELECT DISTINCT firstname
FROM test_tab
GROUP BY firstname
HAVING COUNT(firstname) > 1
union
select firstname from test_tab where ts = (select max(ts) from test_tab)
You can delete from a derived table as long as there is a bijection to the underlying table:
delete from (
select t.*, row_number() over (partition by FIRSTNAME order by ts) as rn
from test_tab t
)
where rn > 1;
Fiddle
Try this.
DELETE FROM TEST_TAB T
WHERE NOT EXISTS
(
SELECT 1
FROM TEST_TAB G
WHERE G.FIRSTNAME = T.FIRSTNAME
HAVING MAX (G.TS) = T.TS
);
SELECT * FROM TEST_TAB;
ID
FIRSTNAME
TS
3
Jhon
2018-06-19 00:10:56.000000
6
Mike
2018-06-20 00:10:56.000000
9
Lis
2018-06-21 00:10:56.000000
fiddle

Get first and last record for each group

I have 2 tables like
My output should look like this. Basically, latest and the first status for each user_unique_access_id along with the timestamp
I tried the below query but my output has duplicated records.
select
distinct u.user_unique_access_id,
first_value(ul.status) over (partition by u.user_unique_access_id
order by
ul.created_timestamp asc) as first_status,
min(ul.created_timestamp) as first_created_at,
first_value(ul.status) over (partition by u.user_unique_access_id
order by
ul.created_timestamp desc) as current_status,
max(ul.created_timestamp) as last_created_at
from
public.users u
join public.user_location ul on
u.user_key = ul.user_key
group by
u.user_unique_access_id,
u.user_name,
ul.status,
ul.created_timestamp ;
DDL and DML statement
create table public.users ( user_key serial primary key, user_name varchar(20), user_unique_access_id varchar(20) );
create table public.user_location( user_key bigint not null, STATUS VARCHAR (512) not null, CREATED_TIMESTAMP timestamp not null, foreign key (user_key) references public.users (user_key) );
insert
into
public.users ( user_unique_access_id, user_name)
values('ABC_1', 'ABC');
insert
into
public.users ( user_unique_access_id, user_name)
values('ABC_2', 'ABC');
insert
into
public.user_location (user_key, status, created_timestamp)
values(1, 'Entrance', current_timestamp);
insert
into
public.user_location (user_key, status, created_timestamp)
values(1, 'Building A', current_timestamp);
insert
into
public.user_location (user_key, status, created_timestamp)
values(1, 'Building B', current_timestamp);
insert
into
public.user_location (user_key, status, created_timestamp)
values(1, 'Exit', current_timestamp);
insert
into
public.user_location (user_key, status, created_timestamp)
values(2, 'Entrance', current_timestamp);
insert
into
public.user_location (user_key, status, created_timestamp)
values(2, 'Building A', current_timestamp);
You can get the "first" and the "last" record using DISTINCT ON. Use that in two derived tables (one for the "first", one for the "last" record) and left join these to users.
SELECT u.user_unique_access_id,
f.status,
f.created_timestamp,
l.status,
l.created_timestamp
FROM users u
LEFT JOIN (SELECT DISTINCT ON (ul.user_key)
ul.user_key,
ul.status,
ul.created_timestamp
FROM user_location ul
ORDER BY ul.user_key ASC,
ul.created_timestamp DESC) f
ON f.user_key = u.user_key
LEFT JOIN (SELECT DISTINCT ON (ul.user_key)
ul.user_key,
ul.status,
ul.created_timestamp
FROM user_location ul
ORDER BY ul.user_key ASC,
ul.created_timestamp ASC) l
ON l.user_key = u.user_key;
(I would have linked a db<>fiddle but your DML is pretty useless as the timestamps are all the same.)

Analytical Query in SQL for MIN, MAX, and AVG

I am trying to figure out a query for this question: for each major, list the number of students, minimum GPA, maximum GPA, average GPA, minimum age, maximum age, and average age. (Show GPA with 2 decimal points, age with no decimal points. You may find it useful to create a view with one of the previous queries for this one.)
This is the script to create the table for SQL!
REM drop all the tables. Note that you need to drop the
REM dependent table first before dropping the base tables.
drop table Reg;
drop table Student;
drop table Course;
REM Now create all the tables.
create table Student
(
sid char(10) primary key,
sname varchar(20) not null,
gpa float,
major char(10),
dob DATE
);
create table Course
(
cno char(10) primary key,
cname varchar(20) not null,
credits int,
dept char(10)
);
create table Reg
(
sid references Student(sid) on delete cascade,
cno references Course(cno) on delete cascade,
grade char(2),
primary key (sid, cno)
);
REM Now insert all the rows.
insert into Student values ('111', 'Joe', 3.5 , 'MIS', '01-AUG-2000');
insert into Student values ('222', 'Jack', 3.4 , 'MIS', '12-JAN-1999');
insert into Student values ('333', 'Jill', 3.2 , 'CS', '15-MAY-1998');
insert into Student values ('444', 'Mary', 3.7 , 'CS', '17-DEC-2001');
insert into Student values ('555', 'Peter', 3.8 , 'CS', '19-MAR-1999');
insert into Student values ('666', 'Pat', 3.9, 'Math', '31-MAY-2000');
insert into Student values ('777', 'Tracy', 4.0, 'Math', '18-JUL-1997');
insert into Course values ('c101', 'intro', 3 , 'CS');
insert into Course values ('m415', 'database', 4 , 'Bus');
insert into Course values ('m215', 'programming', 4 , 'Bus');
insert into Course values ('a444', 'calculus', 3 , 'Math');
insert into Reg values ('111', 'c101', 'A');
insert into Reg values ('111', 'm215', 'B');
insert into Reg values ('111', 'm415', 'A');
insert into Reg values ('222', 'm215', 'A');
insert into Reg values ('222', 'm415', 'B');
insert into Reg values ('333', 'c101', 'A');
insert into Reg values ('444', 'm215', 'C');
insert into Reg values ('444', 'm415', 'B');
insert into Reg values ('555', 'c101', 'B');
insert into Reg values ('555', 'm215', 'A');
insert into Reg values ('555', 'm415', 'A');
insert into Reg values ('666', 'c101', 'A');
This is what I have so far:
SELECT major,
count(distinct SID) as students,
round(min(gpa), 2),
round(max(gpa), 2),
round(avg(gpa), 2),
trunc(min(sysdate - dob)/365) as min_age,
trunc(max(sysdate - dob)/365) as max_age,
trunc(avg(sysdate - dob)/365) as avg_age,
FROM Student
GROUP BY MAJOR;
According to your input I've made a query that I belive will show you the results. (It was kind hard to read the tables the way you posted it). The syntax may differ according to your DBMS (SQL Server, MySQL, REdshift, Postgres, etc)
Here is the query:
SELECT major,
COUNT(*) as students,
ROUND(MIN(gpa), 2) as min_gpa,
ROUND(MAX(gpa), 2) as max_gpa,
ROUND(AVG(gpa), 2) as avg_gpa,
MIN(DATEDIFF(year, current_date, dob)) as min_age,
MAX(DATEDIFF(year, current_date, dob)) as max_age,
AVG(DATEDIFF(year, current_date, dob)) as avg_date
FROM students st left join Course co on co.dept = st.major
GROUP BY major
Your query is completely fine (just remove comma(,) after avg_age.
SELECT major,
count(distinct SID) as students,
round(min(gpa), 2) as MinGPA,
round(max(gpa), 2) as MaxGPA,
round(avg(gpa), 2) as AvgGPA,
round(min(sysdate - dob)/365,0) as min_age,
round(max(sysdate - dob)/365,0) as max_age,
round(avg(sysdate - dob)/365,0) as avg_age
FROM Student
GROUP BY MAJOR;
You can also use months_between() with floor() to get the same result:
select * from student;
SELECT major,
count(distinct SID) as students,
round(min(gpa), 2) as MinGPA,
round(max(gpa), 2) as MaxGPA,
round(avg(gpa), 2) as AvgGPA,
floor(min(months_between(trunc((sysdate)), dob)) /12) as min_age,
floor(max(months_between(trunc((sysdate)), dob)) /12) as max_age,
floor(avg(months_between(trunc((sysdate)), dob)) /12) as avg_age
FROM Student
GROUP BY MAJOR;

How to insert multiple rows in H2 database with the same IDENTITY() value

The following SQL produces unexpected results when executed on H2:
create table TB_RFD_COUNTRY (
ID bigint generated by default as identity,
CREATED_DATE timestamp not null,
MODIFIED_DATE timestamp,
ALPHA_CODE_2 varchar(255) not null,
ALPHA_CODE_3 varchar(255) not null,
NUMERIC_CODE integer not null,
primary key (ID)
);
create table TB_RFD_COUNTRY_VALUE (
ID bigint generated by default as identity,
CREATED_DATE timestamp not null,
MODIFIED_DATE timestamp,
LANGUAGE varchar(2) not null,
VALUE varchar(255) not null,
COUNTRY_ID bigint,
primary key (ID)
);
INSERT INTO TB_RFD_COUNTRY (ALPHA_CODE_2, ALPHA_CODE_3, NUMERIC_CODE, CREATED_DATE) VALUES ('AF', 'AFG', '4', current_timestamp);
INSERT INTO TB_RFD_COUNTRY_VALUE (LANGUAGE, VALUE, COUNTRY_ID, CREATED_DATE) VALUES
('en', 'Afghanistan', IDENTITY(), current_timestamp),
('fr', 'Afghanistan (l'')', IDENTITY(), current_timestamp);
INSERT INTO TB_RFD_COUNTRY (ALPHA_CODE_2, ALPHA_CODE_3, NUMERIC_CODE, CREATED_DATE) VALUES ('AX', 'ALA', '248', current_timestamp);
INSERT INTO TB_RFD_COUNTRY_VALUE (LANGUAGE, VALUE, COUNTRY_ID, CREATED_DATE) VALUES
('en', 'Åland Islands', IDENTITY(), current_timestamp),
('fr', 'Åland(les Îles)', IDENTITY(), current_timestamp);
INSERT INTO TB_RFD_COUNTRY (ALPHA_CODE_2, ALPHA_CODE_3, NUMERIC_CODE, CREATED_DATE) VALUES ('AL', 'ALB', '8', current_timestamp);
INSERT INTO TB_RFD_COUNTRY_VALUE (LANGUAGE, VALUE, COUNTRY_ID, CREATED_DATE) VALUES
('en', 'Albania', IDENTITY(), current_timestamp),
('fr', 'Albanie (l'')', IDENTITY(), current_timestamp);
INSERT INTO TB_RFD_COUNTRY (ALPHA_CODE_2, ALPHA_CODE_3, NUMERIC_CODE, CREATED_DATE) VALUES ('DZ', 'DZA', '12', current_timestamp);
INSERT INTO TB_RFD_COUNTRY_VALUE (LANGUAGE, VALUE, COUNTRY_ID, CREATED_DATE) VALUES
('en', 'Algeria', IDENTITY(), current_timestamp),
('fr', 'Algérie (l'')', IDENTITY(), current_timestamp);
The first IDENTITY() call returns NULL. The second call returns the ID of the English translation inserted in TB_RFD_COUNTRY_VALUE.
This sort of SQL statements work under MySQL or HSQLDB. Is there a way to make them work under H2 or do I need to store the IDENTITY() result in a variable?

Insert same data multiple times

I have an insert statement similar to this:
insert into table (id, name, descr) values (4, 'asdf', 'this is not a word');
I need to insert this same statement with multiple ids. Right now I have:
insert into table (id, name, descr) values (4, 'asdf', 'this is not a word');
insert into table (id, name, descr) values (6, 'asdf', 'this is not a word');
insert into table (id, name, descr) values (7, 'asdf', 'this is not a word');
insert into table (id, name, descr) values (9, 'asdf', 'this is not a word');
Am I just going to have to run this, or is there a more condensed version?
Use a select . . . insert:
insert into table(id, name, descr)
select i.id, 'asdf', 'this is not a word'
from (select 4 as id from dual union all
select 6 from dual union all
select 7 from dual union all
select 9 from dual
) i;
You can use the INSERT ALL statement
INSERT ALL
INTO table (id, name, descr) VALUES (4, 'asdf', 'this is not a word')
INTO table (id, name, descr) VALUES (6, 'asdf', 'this is not a word')
INTO table (id, name, descr) VALUES (7, 'asdf', 'this is not a word')
INTO table (id, name, descr) VALUES (9, 'asdf', 'this is not a word')
SELECT * FROM dual;
INSERT INTO [TableName] (id, name, descr) VALUES
(4, 'asdf', 'this is not a word'),
(6, 'asdf', 'this is not a word'),
(7, 'asdf', 'this is not a word'),
(9, 'asdf', 'this is not a word')
For the sake of argument, one could create a more permanent solution if that ID is also the primary_key by creating a sequence, adding a BEFORE INSERT trigger to the table to increment the ID using the sequence automatically, then loop, inserting however many rows you want and let the ID increment itself:
-- Create the table
CREATE TABLE SEQ_TEST
(
ST_ID NUMBER,
ST_NAME VARCHAR2(50 BYTE),
ST_DESC CHAR(100 BYTE)
);
-- Create the sequence
CREATE SEQUENCE SEQ_TEST_SEQ
START WITH 1
MAXVALUE 9999999999999999999999999999
MINVALUE 0
NOCYCLE
NOCACHE
ORDER;
-- Create the before insert trigger
CREATE OR REPLACE TRIGGER SEQ_TEST_BI
BEFORE INSERT
ON SEQ_TEST
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
if :old.ST_ID is null then
:new.ST_ID := SEQ_TEST_SEQ.nextval;
end if;
END SEQ_TEST_BI;
-- insert 25 rows using an anonymous block. Note the ID is NULL
-- which causes the trigger to increment ID
-- based on the sequence.
begin
for i in 1..25
loop
-- NOTE - Technically you could omit the 'ST_ID' and NULL and it would
-- still work, but I prefer to keep them here to show this action
-- of inserting NULL is intentional and show that all columns are
-- accounted for in the insert.
insert into SEQ_TEST (ST_ID, ST_NAME, ST_DESC) values (NULL, 'asdf', 'this is not a word');
end loop;
end;
commit;
-- Prove it.
select * from seq_test;