Updating multiple rows in PGSQL to the same value - postgresql

I apologize if this has been asked before, but I googled and could not come up with a good search to find out.
I have a table that looks like this:
col_a, col_b
a, 1,
a, 2,
a, 3,
b, 1,
b, 2,
c, 1,
I need to update the values in col_a using a function that generates a random code value, but all values of 'a' need to update to the same value, same for 'b' etc. Is there a systematic way to do this, other than:
UPDATE tbl SET col_a = make_code() WHERE col_a = 'a';
UPDATE tbl SET col_a = make_code() WHERE col_a = 'b'; ...

If make_code() truly work like random(), multiple update statements does not guarantee the same code per update statements.
You need to calculate a code before the real update takes place (for each distinct original code). You can do it with a sub-query, or (more readable with) a CTE:
with codes as (
select distinct on (col_a)
col_a original_code,
make_code() generated_code
from tbl
)
update tbl
set col_a = generated_code
from codes
where col_a = original_code;
SQLFiddle

Related

PostgreSQL: replace generate_series with an array

I have a working SQL code that creates geometries according to numbers generated from a generate_series function:
CREATE TEMPORARY TABLE catchments ON COMMIT DROP AS (
SELECT lims, ST_ConcaveHull(the_geom, alpha_factor) AS the_geom_overlap FROM (
SELECT lims, ST_MakeValid(ST_Collect(n.the_geom)) AS the_geom
FROM generate_series(1, 10, 2) AS lims, pgr_drivingDistance(
'SELECT id, source, target, cost, reverse_cost FROM edges',
vertex_id, lims, true
) a, nodes n WHERE a.node = n.vid
GROUP BY lims
) AS conv_hull
ORDER BY lims DESC
);
Now I need to replace the fixed interval series by an array with varying intervals, e.g. [1, 2, 5, 7, 8].
Is there a simple way to "convert" the generate_series by an array with the same logic? I would like to avoid using a for loop if possible.
FROM unnest(ARRAY[1,2,5,7,8]) AS lims
should do it.

Case When Statement - Three columns

Advanced case when statement in T-SQL using three columns
Hope someone can help with the following :)
I have two tables in SQL.
Table 1 has 4 columns - person_id, A, B, and C.
Table 2 has 3 look-up columns - A, B, and C
I want to carry out the following:
Look down Table 1, column A, find value in Table 2, column A
If no value, go to Table 1, column B.
Look down Table 1, column B, find value in Table 2, column B
If no value, go to Table 1, column C
Look down Table 1, column C, find value in Table 2, column C
If no values in any column put 'Null'
So I know how to write a simple case when statement. However, I think that 'case when' only works on one column. I've started to write out the code but need help to get it right.
Select person_id,
case when 1.A is not null then 2.A
when 1.B is not null then 2.B
else 1.C
end as CODE
from table 1
left join table 2
Order by person_id
Would appreciate any help you can give, thank you.
Your sql seems mostly correct, except for the else 1.C.
Since a CASE WHEN will return 1 value, depending on the first positive WHEN criteria.
And a CASE returns NULL as default.
So ELSE null isn't really needed.
The table descriptions do indicate that Table2 only contains 1 row.
If so, then you can cross join them.
SELECT t1.person_id,
CASE
WHEN t1.A IS NOT NULL THEN t2.A
WHEN t1.B IS NOT NULL THEN t2.B
WHEN t1.C IS NOT NULL THEN t2.C
END AS [CODE]
FROM [Table 1] t1
CROSS JOIN (
SELECT TOP 1 A, B, C
FROM [Table 2]
ORDER BY A DESC, B DESC, C DESC
) t2

After doing CTE Select Order By and then Update, Update results are not ordered the same (TSQL)

The code is roughly like this:
WITH cte AS
(
SELECT TOP 4 id, due_date, check
FROM table_a a
INNER JOIN table_b b ON a.linkid = b.linkid
WHERE
b.status = 1
AND due_date > GetDate()
ORDER BY due_date, id
)
UPDATE cte
SET check = 1
OUTPUT
INSERTED.id,
INSERTED.due_date
Note: the actual data has same due_date.
When I ran the SELECT statement only inside the cte, I could get the result, for ex: 1, 2, 3, 4.
But after the UPDATE statement, the updated results are: 4, 1, 2, 3
Why is this (order-change) happening?
How to keep or re-order the results back to 1,2,3,4 in this same 1 query?
In MSDN https://msdn.microsoft.com/pl-pl/library/ms177564(v=sql.110).aspx you can read that
There is no guarantee that the order in which the changes are applied
to the table and the order in which the rows are inserted into the
output table or table variable will correspond.
Thats mean you can't solve your problem with only one query. But you still can use one batch to do what you need. Because your output don't guarantee the order then you have to save it in another table and order it after update. This code will return your output values in order that you assume:
declare #outputTable table( id int, due_date date);
with cte as (
select top 4 id, due_date, check
from table_a a
inner join table_b b on a.linkid = b.linkid
where b.status = 1
and due_date > GetDate()
order by due_date, id
)
update cte
set check = 1
output inserted.id, inserted.due_date
into #outputTable;
select *
from #outputTable
order by due_date, id;

Concatenated columns should not match in 2 tables

I'll just put this in layman's terms since I'm a complete noobie:
I have 2 tables A and B, both having 2 columns of interest namely: employee_number and salary.
What I am looking to do is to extract rows of 'combination' of employee_number and salary from A that are NOT present in B, but each of employee_number and salary should be present in both.
I am looking to doing it with the 2 following conditions(please forgive the wrong function
names.. this is just to present the problem 'eloquently'):
1.) A.unique(employee_number) exists in B.unique(employee_number) AND A.unique(salary)
exists in B.unique(salary)
2.) A.concat(employee_number,salary) <> B.concat(employee_number,salary)
Note: A and B are in different databases, so I'm looking to use dblink to do this.
This is what I tried doing:
SELECT distinct * FROM dblink('dbname=test1 port=5432
host=test01 user=user password=password','SELECT employee_number,salary, employee_number||salary AS ENS FROM empsal.A')
AS A(employee_number int8, salary integer, ENS numeric)
LEFT JOIN empsalfull.B B on B.employee_number = A.employee_number AND B.salary = A.salary
WHERE A.ENS not in (select distinct employee_number || salary from empsalfull.B)
but it turned out to be wrong as I had it cross-checked by using spreadsheets and I don't get the same result.
Any help would be greatly appreciated. Thanks.
For easier understanding I left out the dblink.
Because, the first one selects lines in B that equal the employeenumber in A as well as the salery in A, so their concatenated values will equal as well (if you expect this to not be true, please provide some test data).
SELECT * from firsttable A
LEFT JOIN secondtable B where
(A.employee_number = B.employee_number AND a.salery != b.salery) OR
(A.salery = B.salery AND A.employee_number != B.employee_number)
If you have troubles with lines containing nulls, you might also try somthing like this:
AND (a.salery != b.salery OR (a.salery IS NULL AND b.salery IS NOT NULL) or (a.salery IS NOT
NULL and b.salery IS NULL))
I think you're looking for something along these lines.
(Sample data)
create table A (
employee_number integer primary key,
salary integer not null
);
create table B (
employee_number integer primary key,
salary integer not null
);
insert into A values
(1, 20000),
(2, 30000),
(3, 20000); -- This row isn't in B
insert into B values
(1, 20000), -- Combination in A
(2, 20000), -- Individual values in A
(3, 50000); -- Only emp number in A
select A.employee_number, A.salary
from A
where (A.employee_number, A.salary) NOT IN (select employee_number, salary from B)
and A.employee_number IN (select employee_number from B)
and A.salary IN (select salary from B)
output: 3, 20000

T-SQL grouping question

Every once and a while I have a scenario like this, and can never come up with the most efficient query to pull in the information:
Let's say we have a table with three columns (A int, B int, C int). My query needs to answer a question like this: "Tell me what the value of column C is for the largest value of column B where A = 5." A real world scenario for something like this would be 'A' is your users, 'B' is the date something happened, and 'C' is the value, where you want the most recent entry for a specific user.
I always end up with a query like this:
SELECT
C
FROM
MyTable
WHERE
A = 5
AND B = (SELECT MAX(B) FROM MyTable WHERE A = 5)
What am I missing to do this in a single query (opposed to nesting them)? Some sort of 'Having' clause?
BoSchatzberg's answer works when you only care about the 1 result where A=5. But I suspect this question is the result of a more general case. What if you want to list the top record for each distinct value of A?
SELECT t1.*
FROM MyTable t1
INNER JOIN
(
SELECT A, MAX(B)
FROM MyTable
GROUP BY A
) t2 ON t1.A = t2.A AND t1.B = t2.B
--
SELECT C
FROM MyTable
INNER JOIN (SELECT A, MAX(B) AS MAX_B FROM MyTable GROUP BY A) AS X
ON MyTable.A = X.A
AND MyTable.B = MAX_B
--
WHERE MyTable.A = 5
In this case the first section (between the comments) can also easily be moved into a view for modularity or reuse.
You can do this:
SELECT TOP 1 C
FROM MyTable
WHERE A = 5
ORDER BY b DESC
I think you are close (and what you have would work). You could use something like the following:
select C
, max(B)
from MyTable
where A = 5
group by C
After a little bit of testing, I don't think that this can be done without doing it the way you're already doing it (i.e. a subquery). Since you need the max of B and you can't get the value of C without also including that in a GROUP BY or HAVING clause, a subquery seems to be the best way.
create table #tempints (
a int,
b int,
c int
)
insert into #tempints values (1, 8, 10)
insert into #tempints values (1, 8, 10)
insert into #tempints values (2, 4, 10)
insert into #tempints values (5, 8, 10)
insert into #tempints values (5, 3, 10)
insert into #tempints values (5, 7, 10)
insert into #tempints values (5, 8, 15)
/* this errors out with "Column '#tempints.c' is invalid in the select list because it is not contained in either an
aggregate function or the GROUP BY clause." */
select t1.c, max(t1.b)
from #tempints t1
where t1.a=5
/* this errors with "An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING
clause or a select list, and the column being aggregated is an outer reference." */
select t1.c, max(t1.b)
from #tempints t1, #tempints t2
where t1.a=5 and t2.b=max(t1.b)
/* errors with "Column '#tempints.a' is invalid in the HAVING clause because it is not contained in either an aggregate
function or the GROUP BY clause." */
select c
from #tempints
group by b, c
having a=5 and b=max(b)
drop table #tempints