PostgreSQL - copy column from related table - sql

So I have three tables: companies, addresses and company_address.
For optimization reasons I need to copy city column from addresses table to companies table. Relation between companies and addresses is many to one (as many companies can occupy same address). They are connected through company_address table, consisting of address_id and company_id columns.
I found this solution for case without intermediate table: How to copy one column of a table into another table's column in PostgreSQL comparing same ID
Trying to modify query I came up with:
UPDATE company SET company.city=foo.city
FROM (
SELECT company_address.company_id, company_address.address_id, address.city
FROM address LEFT JOIN company_address
ON address.id=company_address.address_id
) foo
WHERE company.id=foo.company_id;
but it gives error:
ERROR: column "company" of relation "company" does not exist
I cant figure out what is going on. I'll be grateful for any ideas.

You don't need a subquery for that. Also, refer in the SET clause to your table columns without preceding with table name.
I believe that since your WHERE condition includes joined table, it should be INNER JOIN instead of a LEFT JOIN.
UPDATE company c
SET city = a.city
FROM address a
INNER JOIN company_address ca ON a.id = ca.address_id
WHERE c.id = ca.company_id
Note how using aliases for table names shortens the code and makes it readable at the very first glance.

You're right syntactically, you just don't need the table name at the beginning of the update statement:
UPDATE company SET city=foo.city
FROM (
SELECT company_address.company_id, company_address.address_id, address.city
FROM address LEFT JOIN company_address
ON address.id=company_address.address_id
) foo
WHERE company.id=foo.company_id;

Related

Problem accessing specific column using INNER JOIN

I have two tables Listings & Reviews that both have a column id.
I want to inner join them with Listings.id = Reviews.listing_id but when I am specifying something in my pgadmin, like listings.id or example.example it seems like it doesn't recognise it.
I get the error:
missing FROM-clause entry for table "listings"
Do I write something wrong?
Here is the code:
SELECT
id, listing_url, reviewer_id, reviewer
FROM
public."Listings"
INNER JOIN public."Reviews" ON Listings.id = listing_id
WHERE
reviewer = 'Vivek'
order by
reviewer_id;
Always qualify all columns names so both you and the SQL engine know where the columns come from. It is not clear where the columns come from. I've made guesses on where the columns come from:
SELECT l.id, l.listing_url, r.reviewer_id, r.reviewer
FROM public."Listings" l INNER JOIN
public."Reviews" r
ON l.id = r.listing_id
WHERE r.reviewer = 'Vivek'
ORDER BY r.reviewer_id;
Note that this introduces table aliases, which are abbreviations for tables. This makes it easier to qualify the column names (the default alias is the full table name).
Also, I may be wrong on where the columns come from; your question does not have complete information on the table structure.

Query to output unmatched column names

I have a table with two of the columns lets say Country, Country Code. The table can have multiple rows with same country and code. But country and code always have to match. How can I write a query that will find me a list of all rows where country and country code do not match.
If this is the table, I want the query to return row#4.. where Canada does not match with XYZ (it should have been CN).. There is a master list of country and codes in a different table lets say.. tblCountries.
You can either use the select statement with the NOT EXISTS operator as suggested by Aaron Bertrand in the comments, or you can use a left join:
SELECT T.*
FROM MyTable T
LEFT JOIN TblCountries C ON(T.Country = C.Country AND T.CountryCode = C.CountryCode)
WHERE C.CountryId IS NULL -- Assuming you have a column by that name that's not nullable.
Giorgi Nakeuri's answer will also get you the result you are looking for, however, if there is a record in your table that have a country and a code that doesn't even exist in tblCountry then Aaron's answer or mine will return it, but Giorgi's will not.
First of all if there is another table with contry and code then you are breaking normalization principles having same columns in another table.
You can do it like:
select * from testTable tt
join Countries c on ((c.Country = tt.Country and c.Code <> tt.Code)
or (c.Code = tt.Code and c.Country <> tt.Country))

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.

SQL query to get data from one table based upon a column from another table?

In my tables I have for example
CountyID,County and CityID in the county table and in the city table I have table I have for example
City ID and City
How do I create a report that pulls the County from the county table and pulls city based upon the cityid in the county table.
Thanks
Since this is quite a basic question, I'll give you a basic answer instead of the code to do it for you.
Where tables have columns that "match" each other, you can join them together on what they have in common, and query the result almost as if it was one table.
There are also different types of join based on what you want - for example it might be that some rows in one of the tables you're joining together don't have a corresponding match.
If you're sure that a city will definitely have a corresponding county, try inner joining the two tables on their matching column CityID and querying the result.
The obvious common link between both tables is CityID, so you'd be joining on that. I think you have the data organized wrong though, I'd put CountryID in the City table rather than CityID in the country table. Then, based on the CountryID selected, you can limit your query of the City table based on that.
To follow in context of Bridge's answer, you are obviously new to SQL and there are many places to dig up how to write them. However, the most fundamental basics you should train yourself with is always apply the table name or alias to prevent ambiguity and try to avoid using column names that might be considered reserved words to the language... they always appear to bite people.
That said, the most basic of queries is
select
T1.field1,
T1.field2,
etc with more fields you want
from
FirstTable as T1
where
(some conditional criteria)
order by
(some column or columns)
Now, when dealing with multiple tables, you need the JOINs... typically INNER or LEFT are most common. Inner means MUST match in both tables. LEFT means must match the table on the left side regardless of a match to the right... ex:
select
T1.Field1,
T2.SomeField,
T3.MaybeExistsField
from
SomeTable T1
Join SecondTable T2
on T1.SomeKey = T2.MatchingColumnInSecondTable
LEFT JOIN ThirdTable T3
on T1.AnotherKey = T3.ColumnThatMayHaveTheMatchingKey
order by
T2.SomeField DESC,
T1.Field1
From these examples, you should easily be able to incorporate your tables and their relationships to each other into your results...

Retrieve different row from same table

i hava a set of following tables
customer(cus_id,cus_name);
jointAccount(cus_id,acc_number,relationship);
account(acc_number,cus_id)
now i want to create a select statement to list all the jointAccounts,
it should included the both customer name, and relationship.
I have no idea how to retrieve both different user name, is that possible to do this?
Generally speaking, yes. I'm assuming you mean you want to get customer info for both sides of the joint account per your jointAccount table. Not sure what database you're using so this answer is assuming MySQL.
You can join on the same table twice in a single SQL query. I'm assuming you have not yet created your tables, as you have cus_id listed twice in the jointAccount table. Typically these would be something like cus_id1 and cus_id2, which I've used in my sample query below.
Example:
SELECT c1.cus_id AS cust1_id, c1.cus_name AS cust1_name
, c2.cus_id AS cust2_id, c2.cus_name AS cust2_name, j.relationship
FROM customer c1
INNER JOIN jointAccount j
ON c1.cus_id = j.cus_id1
, customer c2
INNER JOIN jointAccount j
ON c2.cus_id = j.cus_id2
I haven't tested this but that's the general idea.
try this query:
SELECT * FROM jointAccount a LEFT JOIN customer c ON a.cus_id = c.cus_id;
just replace the * with the name of the columns you need.