Problem accessing specific column using INNER JOIN - sql

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.

Related

SQL query wrong index when where on join

I have a query with joins that is not using the index that would be the best match and I am looking for help to correct this.
I have the following query:
select
equipment.name,purchaselines.description,contacts.name,vendors.accountNumber
from purchaselines
left join vendors on vendors.id = purchaselines.vendorId
left join contacts on contacts.id = vendors.contactId
left join equipment on equipment.id = purchaselines.equipmentId
where contacts.id = 12345
The table purchaselines has an index on the column vendorId, which is the proper index to use. When the query is run, I know the value of contacts.id which is joined to vendors.contactId which is joined to purchaselines.vendorId.
What is the proper way to run this query? Currently, no index is used on the table purchaselines.
If you are intending to query a specific contact, I would put THAT first since that is the primary basis. Additionally, you had left-joins to the other tables (vendors, contacts, equipment). So by having a WHERE clause to the CONTACTS table forces the equation to become an INNER JOIN, thus REQUIRING.
That said, I would try to rewrite the query as (also using aliases for simplified readability of longer table names)
select
equipment.name,
purchaselines.description,
contacts.name,
vendors.accountNumber
from
contacts c
join vendors v
on c.id = v.contactid
join purchaselines pl
on v.id = pl.vendorid
join equipment e
on pl.equipmentid = e.id
where
c.id = 12345
Also notice the indentation of the JOINs helps readability (IMO) to see how/where each table gets to the next in a more hierarchical manner. They are all regular inner JOIN context.
So, the customer ID will be the first / fastest, then to vendors by that contact ID which should optimize the join to that. Then, I would expect the purchase lines to have an index on vendorid optimizing that. And finally, the equipment table on ITs PK.
FEEDBACK Basic JOIN clarification.
JOIN is just the explicit statement of how two tables are related. By listing them left-side and right-side and the join condition showing on what relationship is between them is all.
Now, in your data example, each table is subsequently nested under the one prior. It is quite common though that one table may link to multiple other tables. For example an employee. A customer could have an ethnicity ID linking to an ethnicity lookup table, but also, a job position id also linking to a job position lookup table. That might look something like
select
e.name,
eth.ethnicity,
jp.jobPosition
from
employee e
join ethnicitiy eth
on e.ethnicityid = eth.id
join jobPosition jp
on e.jobPositionID = jp.id
Notice here that both ethnicity and jobPosition are at the same hierarchical level to the employee table scenario. If, for example, you wanted to further apply conditions that you only wanted certain types of employees, you can just add your logical additional conditions directly at the location of the join such as
join jobPosition jp
on e.jobPositionID = jp.id
AND jp.jobPosition = 'Manager'
This would get you a list of only those employees who are managers. You do not need to explictily add a WHERE condition if you already include it directly at the JOIN/ON criteria. This helps keeping the table-specific criteria at the join if you ever find yourself needing LEFT JOINs.

What letters are written before column titles

I'm learning SQL language using online resources, but mostly using queries that my predecessors have written at my company. I'm editing fields correspondingly to produce the correct results. But I want to understand more.
I have a few questions about this section of code.
1. Why is there a "p" before TrackingNumber, and oh/cc/im in front of others?
It seems to matter which I choose, so I just use trial and error until it runs.
2. Why do I need to have tracking number - when I delete this line, the code won't run!
select
p.TrackingNumber
,im.Sku
,oh.BusinessUnitCode
,cc.Qty
,oh.ShipCode
,oh.OrigShipCode
,oh.Store
,convert(date,oh.ShipTime) as 'OrderDate'
,oh.ShipToName
,oh.OrderNumber
from dmhost.tblOrderHeader oh
join dmhost.tblContainer c on oh.OrderHeaderID = c.OrderHeaderID
join dmhost.tblPackage p on c.ContainerID = p.ContainerID
join dmhost.tblContainerContents cc on c.ContainerID = cc.ContainerID
join dmhost.tblItemMaster im on im.ItemMasterID = cc.ItemMasterID
where (oh.ShipTime between '04/07/2019' and '05/05/2019')
The letters you talk about are referring to table names (or aliases).
Example using aliases would be:
SELECT c.customerName, o.orderNumber from Customers c
INNER JOIN Orders o on c.id=o.customerid
Same query without aliases:
SELECT Customers.customerName, Orders.orderNumber from Customers
INNER JOIN Orders on Customers.id=Orders.customerid
or omitting table names
SELECT customerName, orderNumber from Customers
INNER JOIN Orders on Customers.id=Orders.customerid
The table denomination is specially important when you retrieve columns with the same name from different table. For example id from Customers and id from Orders
SELECT c.id as CustomerId, o.id as OrderId from Customers c
INNER JOIN Orders o on c.id=o.customerid
The bits before the dot (.) in your field names are table aliases. If you look in the FROM clause of this query you should see these abbreviations in front of the various tables listed in there. They're used to
a) make it less tedious to type table names and
b) make it unambiguous which table you are selecting the column from (this both increases readability of the code and also deals with any cases where two of the tables have columns with the same name)
Here's a simple example of table alias usage:
SELECT emp.ID, emp.Name, dep.ID, dep.Name
FROM employees emp
INNER JOIN departments dep ON dep.ID = emp.DepartmentID
Here you can see that the employees and departments tables have each got aliases to shorten their name. In the query we refer to each field using it's alias. This is especially useful since both tables have fields called "ID" and "Name".
As for why it crashes when you remove p.TrackingNumber, it's likely because you did not also remove the comma (,) from the next line. The comma is used to mark where the name of the next field begins - it could be at the end of the previous line, rather than the start of the next one. Clearly you can't start the list of fields with a comma, because there is no field name preceding it - hence you get a syntax error.
The same query could have been written
select
p.TrackingNumber,
im.Sku,
oh.BusinessUnitCode,
-- etc
which might make it easier to see the usage of the comma.

PostgreSQL - copy column from related table

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;

Viewing Data from two tables while linking multiple tables in SQL

I am trying to set up a view in a database I want to see all the data in the PERSON table and three columns from the NON_PERSONNEL table for a program from the PROGRAM table. This is what I am trying now, the query runs without errors but doesnt give me any results. All 4 of the tables listed below are imperative to derive the answer
SELECT
person.*,
non_personnel.description,
non_personnel.amount
FROM
person,
non_personnel,
personnel_role,
programs
WHERE
person.person_id = personnel_role.person_id
AND personnel_role.program_id = programs.program_id
AND programs.program_id = non_personnel.program_id
AND programs.program_name = 'Fake Program'
you need to use left join, so you get all persons but description and amount can be NULL if that person doesn't have records in other tables
Also use explicit join syntax.
SELECT person.*, non_personnel.description, non_personnel.amount
FROM person
left join personnel_role
ON person.person_id = personnel_role.person_id
left join programs
ON personnel_role.program_id = programs.program_id
AND programs.program_name = 'Fake Program'
left join non_personnel
ON programs.program_id = non_personnel.program_id
This really depends on your schema and the data in the tables. The way you have it written now means that only records that match in every table (according to your WHERE conditions) are passed into the result set.
This means that you have to have all program_id's in your programs table that you want returned in the results ALSO in your non_personnel table. They must also ALL be in your personnel_role table. And all person_ids in your personnel_role table must be in the person table. You get no results back, so this is probably not what you meant to write.
My guess is that you want to use a LEFT OUTER JOIN here. LEFT OUTER JOIN says "Take all records from one table and ONLY records from the joined table that meet the criteria in your ON statement".
Because you are wanting information based on a particular Program, chances are you want to start with that table:
SELECT person.*,
non_personnel.description,
non_personnel.amount
FROM
programs
LEFT OUTER JOIN personnel_role ON
programs.program_id = personnel_role.program_id
LEFT OUTER JOIN person ON
personnel_role.person_id = person.person_id
LEFT OUTER JOIN non_personnel
programs.program_id = non_personnel.program_id
WHERE
programs.program_name = 'Fake Program'
This is a bit of an assumption since I have no idea what your schema is or how your data is built, but I'm betting it's what you are after.
What this FROM clause says is:
1. Take all records from Program (where program_name = 'fake program') and only reocrds from personnel_role that share the same program_id
2. Take only the records from person where the person_id matches the records we just got from the personnel_role table
3. Take only the records from non_personnel where it shares a program_id with the results from the program table.

Select based on the number of appearances of an id in another table

I have a table B with cids and cities. I also have a table C that has these cids with extra information. I want to list all the cids in table C that are associated with ALL appearances of a given city in Table B.
My current solution relies on counting the number of times the given city appears in Table B and selecting only the cids that appear that many times. I don't know all the SQL syntax yet, but is there a way to select for this kind of pattern?
My current solution:
SELECT Agents.aid
FROM Agents, Customers, Orders
WHERE (Customers.city='Duluth')
AND (Agents.aid = Orders.aid)
AND (Customers.cid = Orders.cid)
GROUP BY Agents.aid
HAVING count(Agents.aid) > 1
It only works because I know right now with the HAVING statement.
Thanks for the help. I wasn't sure how to google this problem, since it's pretty specific.
EDIT: I'm pinpointing my problem a bit. I need to know how to determine if EVERY row in a table has a certain value for a field. Declaring a variable and counting the rows in a sub-selection and filtering out my results by IDs that appear that many times works, but It's really ugly.
There HAS to be a way to do this without explicitly count()ing rows. I hope.
Not an answer to your question, but a general improvement.
I'd recommend using JOIN syntax to join your tables together.
This would change your query to be:
SELECT Agents.aid
FROM Agents
INNER JOIN Orders
ON Agents.aid = Orders.aid
INNER JOIN Customers
ON Customers.cid = Orders.cid
WHERE Customers.city='Duluth'
GROUP BY Agents.aid
HAVING count(Agents.aid) > 1
What variant of SQL are you using?
To start with, you can (and should) use JOIN instead of doing it in the WHERE clause, e.g.,
select Agents.aid
from Agents
join Orders on Agents.aid = Orders.aid
join Customers on Customers.cid = Orders.cid
where Customers.city = 'Duluth'
group by Agents.aid
having count(Agents.aid) > 1
After that, I'm afraid I might be a little lost. Using the table names in your example query, what (in English, not pseudocode) are you trying to retrieve? For example, I think your sample query is retrieving the PK for all Agents that have been involved in at least 2 Orders involving Customers in Duluth.
Also, some table definitions for Agents, Orders, and Customers might help (then again, they might be irrelevant).
I'm not sure if I understood you problem, but I think the following query is what you want:
SELECT *
FROM customers b
INNER JOIN orders c USING (cid)
WHERE b.city = 'Duluth'
AND NOT EXISTS (SELECT 1
FROM customers b2
WHERE b2.city = b.city
AND b2.cid <> cid);
Probably you will need some indexes on these columns.