Chaining multiple queries in SQL - sql

I'm using postgresql if that matters.
I need to write into 2 different tables when a new user is created and I want to make sure either both queries fail or both of them succeed. If one of them fails, the other should not succeed.
Lets say I've a table named users and another table named houses and whenever a user is created I give them a house.
user table:
userid(int,auto generated) , username(text), password(text)
houses table:
userid(int) , houseid(int)
when a new user is created, I should call following code:
INSERT INTO users (username , password ) VALUES ('a value', 'a value') RETURNING userid;
INSERT INTO houses (userid, houseid ) VALUES ('returned_value' , 'a value');
but if first query succeeds and the second one fails for whatever reason, that means this will create a user without home. How can I chain these 2 queries together so either both of them succeeds or both of them fails.

Transaction is the way you manage what you want to do here, I mean have one global commit only.
With postgresql, the syntax for managing transactions is the following:
BEGIN;
INSERT INTO users (username , password ) VALUES ('a value', 'a value') RETURNING userid;
INSERT INTO houses (userid, houseid ) VALUES ('returned_value' , 'a value');
COMMIT;

Related

How to store a return value of an SQL query inside a variable

Given a users and a roles table, I want to write an SQL statement, which inserts a new row into the users table and associates a row of the roles table with it.
This is what I have tried:
INSERT INTO users(firstname, lastname) VALUES ('John', 'Smith') RETURNING id;
INSERT INTO roles(role, user_id)
VALUES ('ROLE_USER', id);
The id used in the last line is not yet associated with the return value of the first line. Can someone explain to me how I could store the return type of the first line inside a variable and then use it in my last line?
I have come across the DECLARE keyword, but I am not sure this works with every kind of database. I am using a postgres DB. When using DECLARE #VariableName, the # gets marked as wrong syntax in my db-migrate script.
You can use a data modifying CTE:
with new_user as (
INSERT INTO users(firstname, lastname)
VALUES ('John', 'Smith')
RETURNING id
)
INSERT INTO roles(role, user_id)
SELECT 'ROLE_USER', id
FROM new_user;

Using Sequence for inserting

I want to insert a new row in my table.
There I want to put the ID with the help of seq_name.nextval.
So how to know the sequence name for that particular table?
To use a sequence to generate IDs, you create it normally in the schema where the table is.
As user application user GEM_APP:
CREATE TABLE my_table (id NUMBER, col1 ...);
CREATE SEQUENCE my_seq;
The application user itself (and f.i. it's stored procedures) can use the sequence directly:
INSERT INTO my_table (id, col1) VALUES (my_seq.nextval, 'bla');
However, other users need the correct privileges. Usually, you grant select rights on the sequence to the same users or roles you grant insert rights on the table:
GRANT SELECT, INSERT ON my_table TO user_xy;
GRANT SELECT ON my_seq TO user_xy;
Then the other user can insert data into the table, but must qualify the schema:
INSERT INTO gem_app.my_table(id, col1) VALUES (gem_app.my_seq.nextval, 'bla');
You can create aliases to hide the schemas, some people like them, some not, but I would definitely not recommend to use PUBLIC synonyms as they are hard to control and create all kind of namespace clashes.

need to combine 3 sql-statement into 1

I have 3 SQL-Statements that I would like to combine into just one so I dont have to make multiple requests to my database from my programm (java).
My DB is PostgreSQL 9.4
First one creates a new user in umgmt_users
INSERT INTO umgmt_users ("user") VALUES ('test1')
Second one gets the id of that user (db is postgres and id data type is serial, so it get assigned automatically with me/the programm not knowing what id the user will get
SELECT umgmt_users.id
FROM umgmt_users
WHERE umgmt_users.user = 'test1'
Thrird is to add the just created user with his id (which I need the second statement for) and some other values into a different table
INSERT INTO
umgmt_user_oe_fac_role ("user_id", "oe_id", "fac_id", "role_id")
VALUES ('ID OF USER test1 created in first statement', '1', '2', '1');
Is there a way to get all three Statements into one?
create user
look up the ID he got assigned
insert his ID + other values into a different table
I'm not that good at SQL, I tried to put brackets around the select and put it into the insert & also looked at UNION and WITH but can not get it to work...
EDIT: Ended up using this solution from a_horse_with_no_name
with new_user as (
INSERT INTO umgmt_users ("user") VALUES ('test1')
returning id
)
INSERT INTO umgmt_user_oe_fac_role (user_id, oe_id, fac_id, role_id)
SELECT id, 1, 2, 1
FROM new_user;
All you need is two inserts:
INSERT INTO umgmt_users ("user") VALUES ('test1');
INSERT INTO umgmt_user_oe_fac_role (user_id, oe_id, fac_id, role_id)
VALUES (lastval(), 1, 2, 1);
In order for lastval() to work correctly there must be no other statement between the two inserts and the have to be run in a single transaction (so autocommit needs to be turned off)
Alternatively you can use a data modifying CTE which is then executed as a single statement:
with new_user as (
INSERT INTO umgmt_users ("user") VALUES ('test1')
returning id
)
INSERT INTO umgmt_user_oe_fac_role (user_id, oe_id, fac_id, role_id)
SELECT id, 1, 2, 1
FROM new_user;
Please don't put numbers in single quotes.
The answer to this is : It's impossible to combine these into a single plain vanilla ANSI SQL statement.
The first and third ones talk about two different tables altogether.
The second one is a Select Statement which is a different type of statement from the other two.

PostgreSQL query using table without permission

I am dealing with a remote data base where I have permission to only access specific tables.
The relevant data has the form:
CREATE TABLE t_a(v_a VARCHAR(32), v_b VARCHAR(32));
CREATE TABLE t_b(v_b VARCHAR(32), v_c VARCHAR(32));
INSERT INTO t_a VALUES ('one', 'abc1');
INSERT INTO t_a VALUES ('two', 'abc2');
INSERT INTO t_a VALUES ('three', 'abc3');
INSERT INTO t_b VALUES ('abc1', 'eins');
INSERT INTO t_b VALUES ('abc2', 'zwei');
INSERT INTO t_b VALUES ('abc3', 'drei');
My query looks like:
SELECT DISTINCT ON (v_a) v_a, v_c FROM t_a, t_b WHERE t_a.v_b = t_b=v_b;
Now the actual question: Can I somehow get the same information without permission to read from t_b? Is there another approach I could try?
EDIT
Sadly, I do not have the rights to change the permissions. My line of thought was that since I only want to have an association between v_a and v_c, I could get away with not selecting any columns from t_b. After a bit more thinking, I can see why this should not be allowed by the permission system - after all, I try to read some information from t_b.
I was also hoping that maybe there are different permission layers where one of them permits non-selecting queries.
You can create that table with a different account that either owns it or has been GRANTED read to the table. You than create a stored function created by the same account that owns or has rights to read the table. GRANT EXECUTE to the new function to you personal account that doesn't have rights to the table. Execute the new function and you can get at the data. This is called SECURITY DEFINER and more can be read here http://www.postgresql.org/docs/9.3/static/sql-createfunction.html
With this setup your account won't have access to the table directly, like by doing select * from your_table. You can only get to it via the function. Same can be done with views and is how a lot of database designs setup access to tables. The function or view is the public interface

Complicated/Simple SQL Insert: adding multiple rows

I have a table connecting principals to their roles. I have come upon a situation where I need to add a role for each user. I have a statement SELECT id FROM principals which grabs a list of all the principals. What I want to create is something like the following:
INSERT INTO role_principal(principal_id,role_id)
VALUES(SELECT id FROM principals, '1');
so for each principal, it creates a new record with a role_id=1. I have very little SQL experience, so I dont know if I can do this as simply as I would like to or if there is some sort of loop feature in SQL that I could use.
Also, this is for a mySQL db (if that matters)
Use VALUES keyword if you want to insert values directly. Omit it to use any SELECT (where column count and type matches) to get the values from.
INSERT INTO role_principal(principal_id,role_id)
(SELECT id, 1 FROM principals);
To avoid duplicates is useful to add a subquery :
INSERT INTO role_principal(principal_id,role_id)
(SELECT id, 1 FROM principals p
WHERE NOT EXISTS
(SELECT * FROM role_principal rp WHERE rp.principal_id=p.id AND role_id=1)
)