How do I insert multiple values into PG with different where clauses? - postgresql

Here is how my data is set up:
Zone
------
zone_id
value
other_id
I am going to have the same zone_id for all of the updates. I am only updating the value, but where the other_id is different in each case.
ie: zone_id: 1, [{value: 10, other_id: 12}, {value: 40, other_id: 17}, ...]
I want to do this all in one statement.
UPDATE zone set value = {value} where zone_id = {id} and other_id = {other}, but I want to set multiple values in the same statement.
How do I do this? Is this possible?

I'm not 100% clear, but I think you are asking to set the value conditionally. So something like this should work:
UPDATE zone
SET value = CASE other_id
WHEN 12 THEN 10
WHEN 17 THEN 40
-- and so on
ELSE value
END
WHERE zone_id = 1

Related

Is it possible to bulk update specific values in postgresql efficiently?

I have created a pipeline which is required to update a high number of rows in postgres where each row should be updated differently.
After looking up I found that this could be done using postgres UPDATE.. FROM.. syntax (https://www.postgresql.org/docs/current/sql-update.html) and I came up with the following query that works perfectly fine:
update grades
set course_id = data_table.course_id,
student_id = data_table.student_id,
grade = data_table.grade
from
(select unnest(array[1,2]) as id, unnest(array['Math', 'Math']) as course_id, unnest(array[1000, 1001]) as student_id, unnest(array[95, 100]) as grade) as data_table
where grades.id = data_table.id;
There's also another way to do it with WITH syntax like this:
update grades
set course_id = data_table.course_id,
student_id = data_table.student_id,
grade = data_table.grade
from
(WITH vals (id, course_id, student_id, grade) as (VALUES (1, 'Math', 1000, 95), (2, 'Math', 1001, 100)) SELECT * from vals) as data_table
where grades.id = data_table.id;
My problem is that sometimes I want in some raws to update a field and sometime not. When I don't want to update I just want to keep the value that is currently in the table. In this case, I would want to potentially do something like:
update grades g
set course_id = data_table.course_id,
student_id = data_table.student_id,
grade = data_table.grade
from
(select unnest(array[1,2]) as id, unnest(array[g.course_id, 'Math2']) as course_id, unnest(array[1000, 1001]) as student_id, unnest(array[95, g.grade]) as grade) as data_table
where grades.id = data_table.id;
However this is not possible and I get back the error HINT: There is an entry for table "g", but it cannot be referenced from this part of the query.
Also postgresql documentation specifies about it in the From description:
Note that the target table must not appear in the from_list,
unless you intend a self-join (in which case it must appear with an alias in the from_list).
Does anyone know if there's a way to perform such bulk update ?
I've tried to use JOINs in inner query but with no luck..
Chose a value that cannot be a valid value, eg '-1' for course name and -1 for a grade, and use that for your generated values, then use a case in the insert to direct whether to use the current value or not:
update grades g
set course_id = case when data_table.course_id = '-1' then course_id else data_table.course_id end,
student_id = data_table.student_id,
grade = case when data_table.grade = -1 then g.grade else data_table.grade end
from (
select
unnest(array[1,2]) as id,
unnest(array['-1', 'Math2']) as course_id, -- use '-1' instead of g.course_id
unnest(array[1000, 1001]) as student_id,
unnest(array[95, -1]) as grade -- use -1 instead of g.grade
) as data_table
where grades.id = data_table.id
Pick whatever values you like for the impossible value.
If nulls were not allowed it would have been more straightforward and less code - use null for the impossible value and coalesce() in for the update value.

SQL Table update based on values within table

I am looking to update rows in a table with values from other rows on the same table based on a common value.
In the table below I need to update the rows where 'Effective', 'End' and 'Enddate' are blank with the values from the row with the same IRN and FRN and where Name='[FCA CF] Functions requiring qualifications'.
So row 1 would inherit the data from row 2, row 3 from row 4, etc.
How can I do that please?
Thanks.
Looks like a regular update statement with a self join should do it.
Sample data
create table data
(
IRN int,
FRN int,
Name nvarchar(50),
Effective bit,
Ended bit,
EndDate date
);
insert into data (IRN, FRN, Name, Effective, Ended, EndDate) values
(100, 200, 'Something else?', null, null, null),
(100, 200, '[FCA CF] Functions requiring qualifications', 1, 1, '2020-12-06'),
(100, 200, 'Not what we are looking for', 0, 0, null),
(100, 300, 'Do not touch, different FRN', null, null, null);
Solution
update d1
set d1.Effective = d2.Effective,
d1.Ended = d2.Ended,
d1.EndDate = d2.EndDate
from data d1
join data d2
on d2.IRN = d1.IRN
and d2.FRN = d1.FRN
where d1.Effective is null
and d1.Ended is null
and d1.EndDate is null
and d2.Name = '[FCA CF] Functions requiring qualifications';
Result
IRN FRN Name Effective Ended EndDate
--- --- ------------------------------------------- --------- ----- ----------
100 200 Something else? True True 2020-12-06
100 200 [FCA CF] Functions requiring qualifications True True 2020-12-06
100 200 Not what we are looking for False False null
100 300 Do not touch, different FRN null null null
Fiddle to see it in action.

How to update some columns of multi rows in PostgreSQL

I have a table called Configuration. The 1st column of the table is deviceId, the 2nd column is parameter, 3rd column is value.
In the table, there are many devices, each device has only one device ID(column 1); each device has many config parameters(column 2), ex. VER, DATE, COMPANY etc, all devices have the same config parameters; the parameter's value(column 3) for different devices may be the same or not.
I want to update two config parameters say VER and DATE with new parameter's values(column 3) for the device which has ID equal to "id12345".
How can I achieve this in one PostgreSQL query?
This is what you could do, although I don't think it's a terrific idea.
UPDATE configuration
SET value =
CASE parameter
WHEN 'VER' THEN new_ver_value
WHEN 'DATE' THEN new_date_value
END
WHERE deviceid = 'id12345';
Parameter tables like this are generally considered a bad idea, as the complexity of this query helps illustrate. Also, since you say that all the devices have the same parameters, doing this instead of having a unique column for each parameter doesn't seem to achieve anything useful.
Also if possible use just a number for the deviceid instead of a string.
As requested, to update an additional field, like a time field set to the current_time, you could do the following.
UPDATE configuration
SET value =
CASE parameter
WHEN 'VER' THEN new_ver_value
WHEN 'DATE' THEN new_date_value
END,
time = current_time
WHERE deviceid = 'id12345'
AND parameter IN ('VER', 'DATE');
For the first query, here is a test to show the result.
CREATE TABLE configuration
(deviceid CHARACTER VARYING (20),
parameter CHARACTER VARYING (10),
VALUE integer);
INSERT INTO configuration
(VALUES ('id12345', 'VER', 1),
('id12345', 'DATE', 20190101),
('id12345', 'COMPANY', 55),
('id33333', 'VER', 2),
('id33333', 'DATE', 20180101),
('id33333', 'COMPANY', 6));
SELECT * FROM configuration;
id12345 VER 1
id1234 DATE 20190101
id12345 COMPANY 55
id33333 VER 2
id33333 DATE 20180101
id33333 COMPANY 6
UPDATE configuration
SET value =
CASE parameter
WHEN 'VER' THEN 11
WHEN 'DATE' THEN 2020010
END
WHERE deviceid = 'id12345'
AND parameter IN ('VER', 'DATE');
SELECT * FROM configuration;
id12345 COMPANY 55
id33333 VER 2
id33333 DATE 20180101
id33333 COMPANY 6
id12345 VER 11
id12345 DATE 2020010

SQL 2008 Composite Primary Key With One Part of the Key Auto Increment

Simple table with composite primary key.
PK1 smallint, PK2 smallint auto increment, strValue varchar(20) not null.
This works but PK2 increments independent of PK1.
What I would like is for PK2 to start fresh with a fresh PK1.
Here is what I get
1, 1, 1a
1, 2, 1b
2, 3, 2a
2, 4, 2b
What I want
1, 1, 1a
1, 2, 1b
2, 1, 2a
2, 2, 2b
I take it I need to remove the auto increment from PK2 and generate the proper value on the Insert statement? Is that the correct way to get there and do you recommend syntax for the Insert statement? Auto increment PK1 is not an option as it is part of FK relationship.
Assume I need to insert 2, x, 3b and need to know what value was assigned for x.
There are NOT going to be a lot of inserts (like 100 / day).
There is a unique constraint on PK1, strValue.
To be clear I think SQL 2008 is doing the right thing and am not suggesting that SQL should behave this way by default.
If you're not doing a lot of inserts to the table you can probably remove the identity column and simply do:
IF NOT EXISTS ( SELECT * FROM table WHERE PK1 = 2 AND strValue = '3b' )
BEGIN
DECLARE #next_PK2 INT
SELECT
#next_PK2 = ISNULL(MAX(PK2) + 1, 0) + 1
FROM
table
WHERE
PK1 = 2
INSERT table VALUES (2, #next_PK2, '3b')
END
Yes, if you want to control the incremental value of PK2 you need to generate that during insert (such as with a ROW_NUMBER(). There isn't any other way to set the PK2 independently.

How to get min/max of two integers in Postgres/SQL?

How do I find the maximum (or minimum) of two integers in Postgres/SQL? One of the integers is not a column value.
I will give an example scenario:
I would like to subtract an integer from a column (in all rows), but the result should not be less than zero. So, to begin with, I have:
UPDATE my_table
SET my_column = my_column - 10;
But this can make some of the values negative. What I would like (in pseudo code) is:
UPDATE my_table
SET my_column = MAXIMUM(my_column - 10, 0);
Have a look at GREATEST and LEAST.
UPDATE my_table
SET my_column = GREATEST(my_column - 10, 0);
You want the inline sql case:
set my_column = case when my_column - 10 > 0 then my_column - 10 else 0 end
max() is an aggregate function and gets the maximum of a row of a result set.
Edit: oops, didn't know about greatest and least in postgres. Use that instead.