PostgreSQL UPDATE JOIN ... How does it work? - postgresql

I'm having trouble understanding UPDATE with some sort of JOIN in PostgreSQL
I have the following table (names), sometimes a synonym is filled in the 3rd column:
id,name,synm,sd
41,xgvf
24,y4tg
32,zagr,xgvf
48,argv,bvre
53,bvre
I like to fill column 4 (sd) with the 'parent' id (sd column is empty now)
id,name,synm,sd
41,xgvf
24,y4tg
32,zagr,xgvf,41
48,argv,bvre,53
53,bvre
I tried the following sql statement (and many similar version of it) ...
update names
set sd =
(select n2.id from names n1
inner join names n2
on
n1.synm = n2.name);
... i get the following error:
ERROR: more than one row returned by a subquery used as an expression
SQL state: 21000
I understand my current wrong SQL tries to fill one sd row with all found id's. So somehow I do not get it.
How do I fill the synonym id (sd) in the whole table? Perhaps WITH RECURSIVE like statements?

You can simulate the join like this:
update names n1
set sd = n2.id
from names n2
where n2.name = n1.synm;
See the demo.
Results:
| id | name | synm | sd |
| --- | ---- | ---- | --- |
| 41 | xgvf | | |
| 24 | y4tg | | |
| 53 | bvre | | |
| 48 | argv | bvre | 53 |
| 32 | zagr | xgvf | 41 |

Related

Reset column with numeric value that represents the order when destroying a row

I have a table of users that has a column called order that represents the order in they will be elected.
So, for example, the table might look like:
| id | name | order |
|-----|--------|-------|
| 1 | John | 2 |
| 2 | Mike | 0 |
| 3 | Lisa | 1 |
So, say that now Lisa gets destroyed, I would like that in the same transaction that I destroy Lisa, I am able to update the table so the order is still consistent, so the expected result would be:
| id | name | order |
|-----|--------|-------|
| 1 | John | 1 |
| 2 | Mike | 0 |
Or, if Mike were the one to be deleted, the expected result would be:
| id | name | order |
|-----|--------|-------|
| 1 | John | 1 |
| 3 | Lisa | 0 |
How can I do this in PostgreSQL?
If you are just deleting one row, one option uses a cte and the returning clause to then trigger an update
with del as (
delete from mytable where name = 'Lisa'
returning ord
)
update mytable
set ord = ord - 1
from del d
where mytable.ord > d.ord
As a more general approach, I would really recommend trying to renumber the whole table after every delete. This is inefficient, and can get tedious for multi-rows delete.
Instead, you could build a view on top of the table:
create view myview as
select id, name, row_number() over(order by ord) ord
from mytable

SQL parameter table

I suspect this question is already well-answered but perhaps due to limited SQL vocabulary I have not managed to find what I need. I have a database with many code:description mappings in a single 'parameter' table. I would like to define a query or procedure to return the descriptions for all (or an arbitrary list of) coded values in a given 'content' table with their descriptions from the parameter table. I don't want to alter the original data, I just want to display friendly results.
Is there a standard way to do this?
Can it be accomplished with SELECT or are other statements required?
Here is a sample query for a single coded field:
SELECT TOP (5)
newid() as id,
B.BRIDGE_STATUS,
P.SHORTDESC
FROM
BRIDGE B
LEFT JOIN PARAMTRS P ON P.TABLE_NAME = 'BRIDGE'
AND P.FIELD_NAME = 'BRIDGE_STATUS'
AND P.PARMVALUE = B.BRIDGE_STATUS
ORDER BY
id
I want to produce 'decoded' results like:
| id | BRIDGE_STATUS |
|--------------------------------------|------------ |
| BABCEC1E-5FE2-46FA-9763-000131F2F688 | Active |
| 758F5201-4742-43C6-8550-000571875265 | Active |
| 5E51634C-4DD9-4B0A-BBF5-00087DF71C8B | Active |
| 0A4EA521-DE70-4D04-93B8-000CD12B7F55 | Inactive |
| 815C6C66-8995-4893-9A1B-000F00F839A4 | Proposed |
Rather than original, coded data like:
| id | BRIDGE_STATUS |
|--------------------------------------|---------------|
| F50214D7-F726-4996-9C0C-00021BD681A4 | 3 |
| 4F173E40-54DC-495E-9B84-000B446F09C3 | 3 |
| F9C216CD-0453-434B-AFA0-000C39EFA0FB | 3 |
| 5D09554E-201D-4208-A786-000C537759A1 | 1 |
| F0BDB9A4-E796-4786-8781-000FC60E200C | 4 |
but for an arbitrary number of columns.

Selecting value for the latest two distinct columns

I am trying to do an SQL which will return the latest data value of the two distinct columns of my table.
Currently, I select distinct the values of the column and afterwards, I iterate through the columns to get the distinct values selected before then order and limit to 1. These tags can be any number and may not always be posted together (one time only tag 1 can be posted; whereas other times 1, 2, 3 can).
Although it gives the expected outcome, this seems to be inefficient in a lot of ways, and because I don't have enough SQL experience, this was so far the only way I found of performing the task...
--------------------------------------------------
| name | tag | timestamp | data |
--------------------------------------------------
| aa | 1 | 566 | 4659 |
--------------------------------------------------
| ab | 2 | 567 | 4879 |
--------------------------------------------------
| ac | 3 | 568 | 1346 |
--------------------------------------------------
| ad | 1 | 789 | 3164 |
--------------------------------------------------
| ae | 2 | 789 | 1024 |
--------------------------------------------------
| af | 3 | 790 | 3346 |
--------------------------------------------------
Therefore the expected outcome is {3164, 1024, 3346}
Currently what I'm doing is:
"select distinct tag from table"
Then I store all the distinct tag values programmatically and iterate programmatically through these values using
"select data from table where '"+ tags[i] +"' in (tag) order by timestamp desc limit 1"
Thanks,
This comes close, but beware if you have two rows with the same tag share a maximum timestamp you will get duplicates in the result set
select data from table
join (select tag, max(timestamp) maxtimestamp from table t1 group by tag) as latesttags
on table.tag = latesttags.tag and table.timestamp = latesttags.maxtimestamp

Add New Line Character with Multiple Columns in T-SQL

I have a table that has ID, AddrID, and Addr columns.
The ID can be attached to multiple Addr values in which each address has it own ID.
I am trying to make it so that there is a new line to each ID when it has multiple Addresses loaded And not repeat the ID. So in essence not each row for every record.
Hope it makes sense.
This will eventually become an SSRS report.
The desired output would be something as so:
+----+--------+------------+
| ID | AddrID | Addr |
+----+--------+------------+
| 1 | S1 | 123 N St |
| 2 | S2 | 456 S ST |
| | S3 | 789 W ST |
| | S4 | 987 E ST |
| 3 | S1 | 123 N St |
| | S5 | 147 Elm ST |
| | S6 | 258 SQL St |
+----+--------+------------+
I tried to use:
declare #nl as char(2) = char(13) + char(10)
but its just not working.
Presentation should be done in the presentation layer (Reporting Services in this instance) not in the database or query.
You can do this two ways:
Grouping
Add a Row Group on ID and this will happen automatically.
Expression
You can hide the ID field by putting an expression on the Visibility-Hidden property:
=Fields!ID.Value = Previous(Fields!ID.Value)
This hides the ID field if it is the same as the one on the previous row.

Query on multiple postgres hstores combined with or

This is a hardcoded example of what I'm trying to achieve:
SELECT id FROM places
WHERE metadata->'route'='Route 23'
OR metadata->'route'='Route 22'
OR metadata->'region'='Northwest'
OR metadata->'territory'='Territory A';
Metadata column is an hstore column and I'm wanting to build up the WHERE clause dynamically based on another query from a different table. The table could either be:
id | metadata
---------+----------------------------
1647 | "region"=>"Northwest"
1648 | "route"=>"Route 23"
1649 | "route"=>"Route 22"
1650 | "territory"=>"Territory A"
or
id | key | value
----+-------------+-------+---
1 | route | Route 23
2 | route | Route 22
3 | region | Northwest
4 | territory | Territory A
Doesnt really matter, just whatever works to build up that where clause. It could potentially have 1 to n number of OR's in it based on the other query.
Ended up with a solution using the 2nd table (distribution table):
id | metadata
---------+----------------------------
1647 | "region"=>"Northwest"
1648 | "route"=>"Route 23"
1649 | "route"=>"Route 22"
1650 | "territory"=>"Territory A"
Used the following join, which the #> sees if the places.metadata contains the distributions.metadata
SELECT places.id, places.metadata
FROM places INNER JOIN distributions
ON places.metadata #> distributions.metadata
WHERE distributions.some_other_column = something;