HSQLDB - ON DUPLICATE KEY UPDATE, fails when UPDATE refers to SELECT columns - sql

I am using HSQLDB 2.6.1. I am trying to use ON DUPLICATE KEY UPDATE in a stored procedure. Specifically, I want the UPDATE portion to refer to column values from the SELECT portion.
create table car(
id int,
quantity int,
primary key(id)
);
insert into car values
(1, 100),
(2, 200),
(3, 300);
create procedure insert_update_quantities(in new_quantity int)
modifies sql data
begin atomic
declare table car_quantities (car_id int, car_quantity int);
insert into car_quantities values
(1, new_quantity),
(2, new_quantity),
(3, new_quantity),
(4, new_quantity),
(5, new_quantity);
insert into car (id, quantity)
select car_id, car_quantity from car_quantities
on duplicate key update quantity = quantity + car_quantity;
end;
The above code throws an error when I try to create the procedure. The error says that car_quantity (in the UPDATE portion) is not recognized. When I replace car_quantity with a number, the procedure is created.
I have seen this answer and this documentation, and it seems to me like my code was supposed to work.
What is wrong with my code?

The INSERT is for the table car. The new values for the column quantity can be specified using VALUES(quantity)
create procedure insert_update_quantities(in new_quantity int)
modifies sql data
begin atomic
declare table car_quantities (car_id int, car_quantity int);
insert into car_quantities values
(1, new_quantity),
(2, new_quantity),
(3, new_quantity),
(4, new_quantity),
(5, new_quantity);
insert into car (id, quantity)
select car_id, car_quantity from car_quantities
on duplicate key update quantity = quantity + values(quantity);
end;

Related

PostgreSQL Databases

I have created a table 'DayPass':
CREATE TABLE DayPass (
memberNo INT PRIMARY KEY, FOREIGN KEY (memberNo) REFERENCES DayPass(memberNo),
startDate Date,
numberDays INT,
price VARCHAR(30),
check(numberDays > 0)
);
I am trying to insert these values:
INSERT INTO DayPass (memberNo, startDate, numberDays, price)
VALUES (3, '2022-01-01', '5', '£9.99');
INSERT INTO DayPass
VALUES (3, '2022-02-01', '5', '£9.99');
INSERT INTO DayPass
VALUES (3, '2022-03-01', '£5', '£9.99');
SELECT * FROM DayPass;
but Postgres gives me an error:
ERROR: duplicate key value violates unique constraint "daypass_pkey"
if am unsure where i am going wrong

Sample observations per group without replacement in SQL

Using the provided table I would like to sample let's say 2 users per day so that users assigned to the two days are different. Of course the problem I have is more sophisticated, but this simple example gives the idea.
drop table if exists test;
create table test (
user_id int,
day_of_week int);
insert into test values (1, 1);
insert into test values (1, 2);
insert into test values (2, 1);
insert into test values (2, 2);
insert into test values (3, 1);
insert into test values (3, 2);
insert into test values (4, 1);
insert into test values (4, 2);
insert into test values (5, 1);
insert into test values (5, 2);
insert into test values (6, 1);
insert into test values (6, 2);
The expected results would look like this:
create table results (
user_id int,
day_of_week int);
insert into results values (1, 1);
insert into results values (2, 1);
insert into results values (3, 2);
insert into results values (6, 2);
You can use window functions. Here is an example . . . although the details do depend on your database (functions for random numbers vary by database):
select t.*
from (select t.*, row_number() over (partition by day_of_week order by random()) as seqnum
from test t
) t
where seqnum <= 2;

SQL merge statement with multiple conditions

I have a requirement with some business rules to implement on SQL (within a PL/SQL block): I need to evaluate such rules and according to the result perform the corresponding update, delete or insert into a target table.
My database model contains a "staging" and a "real" table. The real table stores records inserted in the past and the staging one contains "fresh" data coming from somewhere that needs to be merged into the real one.
Basically these are my business rules:
Delta between staging MINUS real --> Insert rows into the real
Delta between real MINUS staging--> Delete rows from the real
Rows which PK is the same but any other fields different: Update.
(Those "MINUS" will compare ALL the fields to get equality and distinguise the 3rd case)
I haven't figured out the way to accomplish such tasks without overlapping between rules by using a merge statement: Any suggestion for the merge structure? Is it possible to do it all together within the same merge?
Thank you!
If I understand you task correctly following code should do the job:
--drop table real;
--drop table stag;
create table real (
id NUMBER,
col1 NUMBER,
col2 VARCHAR(10)
);
create table stag (
id NUMBER,
col1 NUMBER,
col2 VARCHAR(10)
);
insert into real values (1, 1, 'a');
insert into real values (2, 2, 'b');
insert into real values (3, 3, 'c');
insert into real values (4, 4, 'd');
insert into real values (5, 5, 'e');
insert into real values (6, 6, 'f');
insert into real values (7, 6, 'g'); -- PK the same but at least one column different
insert into real values (8, 7, 'h'); -- PK the same but at least one column different
insert into real values (9, 9, 'i');
insert into real values (10, 10, 'j'); -- in real but not in stag
insert into stag values (1, 1, 'a');
insert into stag values (2, 2, 'b');
insert into stag values (3, 3, 'c');
insert into stag values (4, 4, 'd');
insert into stag values (5, 5, 'e');
insert into stag values (6, 6, 'f');
insert into stag values (7, 7, 'g'); -- PK the same but at least one column different
insert into stag values (8, 8, 'g'); -- PK the same but at least one column different
insert into stag values (9, 9, 'i');
insert into stag values (11, 11, 'k'); -- in stag but not in real
merge into real
using (WITH w_to_change AS (
select *
from (select stag.*, 'I' as action from stag
minus
select real.*, 'I' as action from real
)
union (select real.*, 'D' as action from real
minus
select stag.*, 'D' as action from stag
)
)
, w_group AS (
select id, max(action) as max_action
from w_to_change
group by id
)
select w_to_change.*
from w_to_change
join w_group
on w_to_change.id = w_group.id
and w_to_change.action = w_group.max_action
) tmp
on (real.id = tmp.id)
when matched then
update set real.col1 = tmp.col1, real.col2 = tmp.col2
delete where tmp.action = 'D'
when not matched then
insert (id, col1, col2) values (tmp.id, tmp.col1, tmp.col2);

Select prices/sizes from tables using joiner table

I am trying to create a product page that shows the product info, as well as prices and sizes. Each product will have multiple prices and sizes.
I have multiple tables (products, sizes, prices) and I am using a "joiner" table that only has those tables as foreign keys. This is where I am getting the most confused.
SCHEMA
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT,
description TEXT,
);
CREATE TABLE sizes (
id SERIAL PRIMARY KEY,
height INT,
width INT,
garmentSize TEXT
);
CREATE TABLE prices (
id SERIAL PRIMARY KEY,
price INT
);
CREATE TABLE product_price_size ( --joiner table
id SERIAL PRIMARY KEY,
productId INTEGER REFERENCES products(id),
priceId INTEGER REFERENCES prices(id),
sizeId INTEGER REFERENCES sizes(id)
);
INSERT INTO products (name, description)
VALUES ('Take A Hike', 'Take A Hike vinyl decal');
INSERT INTO products (name, description)
VALUES ('Wanderlust', 'Wanderlust vinyl decal');
INSERT INTO prices (price) VALUES (4);
INSERT INTO prices (price) VALUES (6);
INSERT INTO prices (price) VALUES (8);
INSERT INTO sizes (height, width, garmentSize) VALUES (3, 3, null);
INSERT INTO sizes (height, width, garmentSize) VALUES (4, 5, null);
INSERT INTO sizes (height, width, garmentSize) VALUES (7, 2, null);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (1, 1, 1);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (1, 2, 2);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (1, 3, 3);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (2, 2, 3);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (2, 2, 2);
INSERT INTO product_price_size (productId, priceId, sizeid) VALUES (2, 1, 3);
I need to get each product to show multiple sizes that has a price associated with it, and I am not sure how get that done. I am using Angular to get to "mysite.com/products/2" which in this example is getting the 2nd product from the products table, so if I currently go to the products page, an error will occur because there are currently three "product.id"s that reference product id 2.
QUERY
SELECT products.name, prices.price, sizes.height, sizes.width FROM products
INNER JOIN product_price_size ON products.id = product_price_size.productId
INNER JOIN prices ON prices.id = product_price_size.priceId
INNER JOIN sizes ON sizes.id = product_price_size.sizeId
I think I might need a subquery, but I'll admit I am lost on this.
Please see SQL Fiddle here
I hope this isn't too confusing, and any thoughts or even better ideas for the way I'm doing it are very appreciated. Thanks!!
Keith

How to insert multiple row values into SQL

I have the following 3 tables:
CREATE TABLE Tests (
Test_ID INT,
TestName VARCHAR(50));
INSERT INTO Tests VALUES (1, 'SQL Test');
INSERT INTO Tests VALUES (2, 'C# Test');
INSERT INTO Tests VALUES (3, 'Java Test');
CREATE TABLE Users (
[User_ID] INT,
UserName VARCHAR(50));
INSERT INTO Users VALUES (1, 'Joe');
INSERT INTO Users VALUES (2, 'Jack');
INSERT INTO Users VALUES (3, 'Jane');
CREATE TABLE UserTests (
ID INT,
[User_ID] INT,
Test_ID INT,
Completed INT);
INSERT INTO UserTests VALUES (1, 1, 1, 0);
INSERT INTO UserTests VALUES (2, 1, 2, 1);
INSERT INTO UserTests VALUES (3, 1, 3, 1);
INSERT INTO UserTests VALUES (4, 2, 1, 0);
INSERT INTO UserTests VALUES (5, 2, 2, 0);
INSERT INTO UserTests VALUES (6, 2, 3, 0);
INSERT INTO UserTests VALUES (7, 3, 1, 1);
INSERT INTO UserTests VALUES (8, 3, 2, 1);
INSERT INTO UserTests VALUES (9, 3, 3, 1);
I would like to create some rule/trigger so that when a new user gets added to the Users table, an entry for each Test and that user's Id will get added to the UserTests table.
Something like this if the new user ID is 5:
INSERT dbo.UserTest
(USER_ID, TEST_ID, Completed)
VALUES
(5, SELECT TEST_ID FROM Tests, 0)
That syntax is of course wrong but to give an idea of what I expect to happen.
So I expect that statement to add these values to the UserTests table:
User ID| Test ID| Completed
5 | 1 | 0
5 | 2 | 0
5 | 3 | 0
You can use after trigger for user table.
Create Trigger tr_user on Users
After Insert
AS Begin
INSERT UserTest(USER_ID, TEST_ID, Completed)
Select I.USER_ID, t.TEST_ID, 0
From Inserted I, Tests t
END
Here's a SQL Fiddle that finds missing records and inserts them.
SQL Fiddle
The SELECT:
select u.user_id, t.test_id, 0 as Completed
from users u
cross join tests t
where not exists (
select 1
from usertests ut
where ut.user_id = u.user_id and ut.test_id = t.test_id)
Adding insert into UserTests (User_Id, Test_Id, Completed) before the select will insert these records.
You can add a user id on to the where clause to do it for a single user if required. It is re-runnable so it won't re-insert test ids for a user that already has them, but will add new ones if new tests are introduced.