Replace values in array column with related values from another table - sql

In my database I have a table relations with a column relation_ids containing the IDs of users (user_id). This takes the form of an array with many IDs possible, e.g.:
{111,112,156,4465}
I have another table names containing information on users such as user_id, first_name, last_name etc.
I would like to create an SQL query to return all rows from relations with all columns, but append the array column relation_ids with first_name from the names table substituted for IDs.
Is it possible as some kind of subquery?

Assuming you want to preserve the order in the array - first names listed in the same order as IDs in the original relation_ids.
I suggest an ARRAY constructor over a correlated subquery with unnest() and WITH ORDINALITY, joined to the names table, like:
SELECT r.*
, (ARRAY (
SELECT n.first_name
FROM unnest(r.relation_ids) WITH ORDINALITY AS a(user_id, ord)
JOIN names n ON n.user_id = a.user_id
ORDER BY a.ord
)
) AS first_names
FROM relations r;
This query preserves all rows from relations in any case.
Corner cases to note:
1. A NULL value in relation_ids (for the whole column) is translated to an empty array. (Same as empty array in the source.)
2. NULL elements are silently dropped from the array.
You might want to define desired behavior if those corner cases are possible ...
db<>fiddle here
Related:
LEFT OUTER JOIN on array column with multiple values
PostgreSQL unnest() with element number
Considered a normalized db design:
Can PostgreSQL array be optimized for join?

This will get you all the columns and rows from Relations with first_name appended from the Names table.
Select Relations.relation_ids, Names.user_id, Names.first_name From Relations
Inner Join Names On Relations.user_id=Names.user_id

Related

Combine two queries in one (one by one and sorted)

I have a table of models with columns
Both columns are of a varchar type that contains an array of strings.
What I need to achieve here is to get all tags separately, without dups and in specific order. First should be system_tags in alphabetical order, then tags in alphabetical order as well
SELECT
unnest(system_tags) as tag_name
FROM
"models"
left join projects on projects.id = models.project_id
where projects.is_public = true
union
SELECT
unnest(tags)
FROM
"models"
left join projects on projects.id = models.project_id
where projects.is_public = true
I got as far as to get all separated tags without duplications, but can I order them one by one with union?
So I can get result as on the first picture instead as it on the second
Use a union query to combine multiple queries into a single result. Sometimes you might want to list the records from one table or query with those from one or more other tables to form one set of records - a list with all the records from the two or more tables. This is the purpose of a union query in Access.

Get distinct ids between multiple columns within the same column in PostgreSQL

So I have a postgresql table that has userids who created a party in one column and an array of userids whom have joined their party.
im wondering if there is any way to return a list of the distinct userids from both the column of their userid and the array, so the distinct list of userids who has either created or joined a party?
If I understand correctly, you can use union:
select created_userid as userid
from t
union -- on purpose to remove duplicates
select joined_userid as userid
from t cross join lateral
unnest(t.joined_userids) joined_userid;

SELECT table.* to consistent names (e.g. table.field)

I have several tables with a bunch of fields and I need a Query that includes ALL the fields from two tables (JOINs). The result should have consistent naming scheme for the fields, that survives when I add fields to the source table.
To always select all fields of a table I can use the * operator. But when I join two tables using the *, it prepends the fields that occur in both tables (and only those) with the table name.
SELECT kids.*, parents.* FROM parents INNER JOIN kids ON parents.ID = kids.ParentID;
gives me
kids.name, birthday, school, parents.name, address ...
When I add a birthday column to the parents table I get
kids.name, kids.birthday, school, parents.name, parents.birthday, address ...
And I have to update birthday to kids.birthday everywhere.
Is there a way to prepend all column names in the beginning?
So I'd get
kids.name, kids.birthday, kids.school, parents.name, parents.address ...
in the first place?

How to extract from Text and match from another query

I have a table with airport sequences (AIRPRT_SERIES) like SFO/ATL/GRU or PIT/ATL/GIG/VIX. I would like to find all matches in this field (in this case ATL/GRU and ATL/GIG) that match another table where I have those flights (Hub: ATL Spoke:GIG). Problem is that I don't know how to pass or join my tables to make this happen.
This is a query that does half of what I want. The problem is that there are no fields in either table that match (other than when I extract them) so I don't know how to do the join.
select
*
from LEG_OD leg
inner join myMarkets mkts
on leg.nondir=mkts.nd_arp -- Current condition but not what I want/need
WHERE
REGEXP_SIMILAR(AIRPRT_SERIES , '[A-Z]{3}/('||mkts.SPOKE||'/'||mkts.Hub||'/|'||mkts.Hub||'/'||mkts.Spoke||'/)[A-Z]{3}' )=1
AND
leg.year_month BETWEEN '20160101' AND '20160112'
LEG_OD Fields: AIRPRT_SERIES, Passengers, nondir
myMarkets: Hub, Spoke, Distance,nd_arp
I would like to keep the REGEXP_SIMILAR condition as this is part of a larger query.
You'll need to use STRTOK_SPLIT_TO_TABLE. Taking your LEG_OD table, we can split out the individual elements from airport_series.
select
d.* from TABLE (strtok_split_to_table (LEG_OD.<some sort of id column>,LEG_OD.AIRPORT_SERIES, '/')
returns (outkey integer, tokennum integer,toke varchar(3) character set unicode)) as d
order by 1,2
That will get you a row for each element in the airport_series column.
If I understand your data correctly, you could then join on those values.

How to get matching data from another SQL table for two different columns: Inner Join and/or Union?

I've got two tables in MS Access that keep track of class facilitators and the classes they facilitate. The two tables are structured as follows:
tbl_facilitators
facilID -> a unique autonumber to keep track of individual teachers
facilLname -> the Last name of the facilitator
facilFname -> the First name of the facilitator
tbl_facilitatorClasses
classID -> a unique autonumber to keep track of individual classes
className -> the name of the class (science, math, etc)
primeFacil -> the facilID from the first table of a teacher who is primary facilitator
secondFacil -> the facilID from the first table of another teacher who is backup facilitator
I cannot figure out how to write an Inner Join that pulls up the results in this format:
Column 1: Class Name
Column 2: Primary Facilitator's Last Name
Column 3: Primary Facilitator's First Name
Column 4: Secondary Facilitator's Last Name
Column 5: Secondary Facilitator's First Name
I am able to pull up and get the correct results if I only request the primary facilitator by itself or only request the secondary facilitator by itself. I cannot get them both to work out, though.
This is my working Inner Join:
SELECT tbl_facilitatorClasses.className,
tbl_facilitators.facilLname, tbl_facilitators.facilFname
FROM tbl_facilitatorClasses
INNER JOIN tbl_facilitators
ON tbl_facilitatorClasses.primeFacil = tbl_facilitators.facilID;
Out of desperation I also tried a Union, but it didn't work out as I had hoped. Your help is greatly appreciated. I'm really struggling to make any progress at this point. I don't often work with SQL.
SOLUTION
Thanks to #philipxy I came up with the following query which ended up working:
SELECT tblCLS.className,
tblP.facilLname, tblP.facilFname, tblS.facilLname, tblS.facilFname
FROM (tbl_facilitatorClasses AS tblCLS
INNER JOIN tbl_facilitators AS tblP
ON tblCLS.primeFacil=tblP.facilID)
INNER JOIN tbl_facilitators AS tblS
ON tblCLS.secondFacil=tblS.facilID;
When performing multiple Inner Joins in MS Access, parenthesis are needed...As described in this other post.
(The following applies when every row is SQL DISTINCT, and outside SQL code similarly treats NULL like just another value.)
Every base table has a statement template, aka predicate, parameterized by column names, by which we put a row in or leave it out. We can use a (standard predicate logic) shorthand for the predicate that is like its SQL declaration.
-- facilitator [facilID] is named [facilFname] [facilLname]
facilitator(facilID, facilLname, facilFname)
-- class [classID] named [className] has prime [primeFacil] & backup [secondFacil]
class(classID, className, primeFacil, secondFacil)
Plugging a row into a predicate gives a statement aka proposition. The rows that make a true proposition go in a table and the rows that make a false proposition stay out. (So a table states the proposition of each present row and states NOT the proposition of each absent row.)
-- facilitator f1 is named Jane Doe
facilitator(f1, 'Jane', 'Doe')
-- class c1 named CSC101 has prime f1 & backup f8
class(c1, 'CSC101', f1, f8)
But every table expression value has a predicate per its expression. SQL is designed so that if tables T and U hold the (NULL-free non-duplicate) rows where T(...) and U(...) (respectively) then:
T CROSS JOIN U holds rows where T(...) AND U(...)
T INNER JOIN U ONcondition holds rows where T(...) AND U(...) AND condition
T LEFT JOIN U ONcondition holds rows where (for U-only columns U1,...)
    T(...) AND U(...) AND condition
OR T(...)
    AND NOT there EXISTS values for U1,... where [U(...) AND condition]
    AND U1 IS NULL AND ...
T WHEREcondition holds rows where T(...) AND condition
T INTERSECT U holds rows where T(...) AND U(...)
T UNION U holds rows where T(...) OR U(...)
T EXCEPT U holds rows where T(...) AND NOT U(...)
SELECT DISTINCT * FROM T holds rows where T(...)
SELECT DISTINCTcolumns to keepFROM T holds rows where
there EXISTS values for columns to drop where T(...)
VALUES (C1, C2, ...)((v1,v2, ...), ...) holds rows where
C1 = v1 AND C2 = v2 AND ... OR ...
Also:
(...) IN T means T(...)
scalar= T means T(scalar)
T(..., X, ...) AND X = Y means T(..., Y, ...) AND X = Y
So to query we find a way of phrasing the predicate for the rows that we want in natural language using base table predicates, then in shorthand using base table predicates, then in shorthand using aliases in column names except for output columns, then in SQL using base table names plus ON & WHERE conditions etc. If we need to mention a base table twice then we give it aliases.
-- natural language
there EXISTS values for classID, primeFacil & secondFacil where
class [classID] named [className]
has prime [primeFacil] & backup [secondFacil]
AND facilitator [primeFacil] is named [pf.facilFname] [pf.facilLname]
AND facilitator [secondFacil] is named [sf.facilFname] [sf.facilLname]
-- shorthand
there EXISTS values for classID, primeFacil & secondFacil where
class(classID, className, primeFacil, secondFacil)
AND facilitator(pf.facilID, pf.facilLname, pf.facilFname)
AND pf.facilID = primeFacil
AND facilitator(sf.facilID, sf.facilLname, sf.facilFname)
AND sf.facilID = secondFacil
-- shorthand using aliases everywhere but result
-- use # to distinguish same-named result columns in specification
there EXISTS values for c.*, pf.*, sf.* where
className = c.className
AND facilLname#1 = pf.facilLname AND facilFname#1 = pf.facilFname
AND facilLname#2 = sf.facilLname AND facilFname#2 = sf.facilFname
AND class(c.classID, c.className, c.primeFacil, c.secondFacil)
AND facilitator(pf.facilID, pf.facilLname, pf.facilFname)
AND pf.facilID = c.primeFacil
AND facilitator(sf.facilID, sf.facilLname, sf.facilFname)
AND sf.facilID = c.secondFacil
-- table names & SQL (with MS Access parentheses)
SELECT className, pf.facilLname, pf.facilFname, sf.facilLname, sf.facilFname
FROM (class JOIN facilitator AS pf ON pf.facilID = primeFacil)
JOIN facilitator AS sf ON sf.facilID = secondFacil
OUTER JOIN would be used when a class doesn't always have both facilitators or something doesn't always have all names. (Ie if a column can be NULL.) But you haven't given the specific predicates for your base table and query or the business rules about when things might be NULL so I have assumed no NULLs.
Is there any rule of thumb to construct SQL query from a human-readable description?
(Re MS Access JOIN parentheses see this from SO and this from MS.)
Just do an extra join for the secondary facilitator (and please use table aliases!):
SELECT fc.className, f1.facilLname, f2.facilFname
FROM tbl_facilitatorClasses fc
INNER JOIN tbl_facilitators f1 ON fc.primeFacil = f1.facilID
INNER JOIN tbl_facilitators f2 ON fc.secondFacil = f2.facilID;
I would do it as above by joining to the tbl_facilitators table twice but you might want to make sure that every class really does require a 2nd facilitator as the second join should be an outer join instead. Indeed it might be safer to assume that it's not a required field.