Use of NATURAL JOIN - sql

According by the task I'm doing I need to change INNER JOIN to NATURAL JOIN and get the same result as this result of inner join
I got that result with this query:
SELECT person_order.order_date,
person.name || ' (' || 'age:' || person.age || ')' AS person_information
FROM person_order
INNER JOIN person ON person_order.person_id = person.id
ORDER BY 1,
2;
Here is what my tables look like my tables
I'm trying to use subquery in a FROM statement but now result is differ with previous query different rusult
SELECT pers_ord.order_date,
person.name || ' (' || 'age:' || person.age || ')' AS person_information
FROM (
SELECT order_date
FROM person_order
) AS pers_ord
NATURAL JOIN person
ORDER BY 1,
2;

Could you try this:
SELECT pers_ord.order_date,
person.name || ' (' || 'age:' || person.age || ')' AS person_information
FROM (
SELECT order_date, person_id AS id
FROM person_order
) AS pers_ord
NATURAL JOIN person
ORDER BY 1,
2;
Looking at examples here you can see the columns on which the join is performed need to be the same. And in your person_order table the person_id column is not matching the id in the person table.

Related

How to unite several tables in a one so the names of the columns became the row names?

for instance I have
SELECT customer_id, first_name || ', ' || last_name || ', ' || email as "customer's info"
FROM customer
WHERE customer_id = 5
;
SELECT count(i.film_id) AS "num.of films rented" FROM payment p
JOIN rental r ON p.rental_id = r.rental_id
JOIN inventory i ON r.inventory_id = i.inventory_id
WHERE r.rental_date >= ('2014-01-01'::date)
AND r.rental_date <= ('2017-05-03'::date)
AND p.customer_id = 5
;
I want in output
metric1 | metric2
----------------------------
customer's info | blalalalal
num.of films rented | blalalalal
I try smth like, but nothing
SELECT * FROM crosstab(
SELECT first_name || ', ' || last_name || ', ' || email
FROM customer WHERE customer_id = 5,
SELECT count(i.film_id) FROM payment p
JOIN rental r ON p.rental_id = r.rental_id
JOIN inventory i ON r.inventory_id = i.inventory_id
WHERE r.rental_date >= ('2014-01-01'::date)
AND r.rental_date <= ('2017-05-03'::date))
AS ('fjfjf' TEXT, 'fjfjf' int );
Could you help me?
I dont know how to do it in postgress
Thanks a lot
I would UNION ALL the two queries together - but remember to CAST the count value as a string, as you need matching data types to UNION:
SELECT
'customer''s info' AS "name"
, first_name || ', ' || last_name || ', ' || email AS "value"
FROM customer c
UNION ALL
'num.of films rented' AS "name"
, COUNT(i.film_id)::VARCHAR(5) AS "value"
FROM payment p
JOIN rental r ON p.rental_id = r.rental_id
JOIN inventory i ON r.inventory_id = i.inventory_id
WHERE r.rental_date >= ('2014-01-01'::date)
AND r.rental_date <= ('2017-05-03'::date)
WHERE customer_id = 5
;
It is unclear to me why inventory is in the second join.
SELECT 'customer''s info' as metric1,
first_name || ', ' || last_name || ', ' || email as metric2
FROM customer
WHERE customer_id = 5
UNION ALL
SELECT 'num.of films rented' as metric1, count(i.film_id)::text AS metric2
FROM payment p JOIN
rental r
ON p.rental_id = r.rental_id
WHERE r.rental_date >= '2014-01-01'::date AND
r.rental_date <= '2017-05-03'::date AND
p.customer_id = 5;
You could also combine this into a single query if you are just trying to get the results in a single result set:
SELECT (first_name || ', ' || last_name || ', ' || email) as customer_info,
count(i.film_id) as num_films
FROM payment p JOIN
rental r
ON p.rental_id = r.rental_id JOIN
customer c
ON c.customer_id = p.customer_id
WHERE r.rental_date >= '2014-01-01'::date AND
r.rental_date <= '2017-05-03'::date AND
c.customer_id = 5
GROUP BY c.customer_id;
(This puts the values in one row with two columns.) Using a subquery, the results can be easily unpivoted.

Join two of the same tables to another table and output the info of the (same) table in the same row

Sorry for the bad/long title but I don't know how else to put it.
What I want to do is join to 'A' tables and join it to the 'B' table where both 'A' have a foreign key in common and display info from both 'A' tables in the same row while preventing duplicates such as the example in the pic:
I know the query is just doing it's job, but is there a way to prevent 'duplicates' by comparing between the rows before output?
Here's what I tried, I know it may be bad performance-wise and there may be better ways but this is for a mini-project with a small DB, where performance shouldn't really matter:
SELECT w.emp_id AS emp1_id, w2.emp_id AS emp2_id,
e.fname || ' ' || e.lname AS emp1_name, e1.fname || ' ' || e1.lname AS emp2_name,
e.jobtitle AS emp1_jobtitle, e1.jobtitle AS emp2_jobtitle, e2.fname || ' ' || e2.lname AS cs_name
FROM work_on w
LEFT JOIN work_on w2
on w.emp_id != w2.emp_id and w.ticket_id = w2.ticket_id
LEFT JOIN employee e
on w.emp_id = e.emp_id
LEFT JOIN employee e1
on w2.emp_id = e1.emp_id
LEFT JOIN ticket t
on t.ticket_id = w.ticket_id
LEFT JOIN customer_problem p
on p.problem_id = t.problem_id
LEFT JOIN employee e2
on e2.emp_id = p.emp_id
WHERE e2.emp_id = 20 and p.submit_date >= '2018-04-08'
and p.submit_date <= '2018-04-11' and e1.emp_id != e.emp_id
ORDER BY w.emp_id;
My tables:
Employee: | Work_On: | Ticket: | Problem
----------+------------+--------------+------------
emp_id work_id ticket_id problem_id
fname emp_id problem_id emp_id
lname ticket_id
In this case I'm trying to combine two Employee on Work_On where they have the Ticket in common and another Employee which connects to the ticket via the Problem table.
Here is one option using least/greatest:
SELECT DISTINCT
LEAST(w.emp_id, w2.emp_id) AS emp1_id,
GREATEST(w.emp_id, w2.emp_id) AS emp2_id,
LEAST(e.fname || ' ' || e.lname, e1.fname || ' ' || e1.lname) AS emp1_name,
GREATEST(e.fname || ' ' || e.lname, e1.fname || ' ' || e1.lname) AS emp2_name,
LEAST(e.jobtitle, e1.jobtitle) AS emp1_jobtitle,
GREATEST(e.jobtitle, e1.jobtitle) AS emp2_jobtitle,
e2.fname || ' ' || e2.lname AS cs_name
FROM work_on w
LEFT JOIN work_on w2
ON w.emp_id != w2.emp_id AND w.ticket_id = w2.ticket_id
LEFT JOIN employee e
ON w.emp_id = e.emp_id
LEFT JOIN employee e1
ON w2.emp_id = e1.emp_id
LEFT JOIN ticket t
ON t.ticket_id = w.ticket_id
LEFT JOIN customer_problem p
ON p.problem_id = t.problem_id
LEFT JOIN employee e2
ON e2.emp_id = p.emp_id
WHERE
e2.emp_id = 20 AND
p.submit_date >= '2018-04-08' AND
p.submit_date <= '2018-04-11' AND
e1.emp_id != e.emp_id
ORDER BY w.emp_id;
To see why the least/greatest trick works, consider the following two records/columns:
emp1_id | emp2_id
2 | 15
15 | 2
It should be clear that while these records are distinct now, if we instead choose the least id followed by the greatest id, they appear identical:
LEAST(emp_id1, emp_id2) | GREATEST(emp_id1, emp_id2)
2 | 15
2 | 15
Then, using SELECT DISTINCT removes one of the two duplicate rows.

Aggregate function as condition

I'm having problem trying to use aggregate function result as a condition. Basically, I need to select rows that have "View count" more than 3. Here is code that works:
SELECT b.BranchNo AS "Branch Number",
p.PropertyNo || ', ' || p.PostCode || ', ' || p.City || ', ' || p.Street AS "Object address" , count(v.ViewDate) as "View count"
FROM Branch b INNER JOIN PropertyForRent p ON b.BranchNo=p.PropertyBranchNo
INNER JOIN Viewing v ON p.PropertyNo=v.ViewPropertyNo WHERE v.ViewDate>='2014-01-01'
GROUP BY b.BranchNo, p.PropertyNo;
I was trying to use something like that:
HAVING count(v.ViewDate)>=3
But that obviously didn't work. Is there a way of making such condition without using a subquery?
It looks like the fields in your select don't match up with the group by, but other than that, the "condition based on aggregate" is exactly what the having clause is for. For example, this query should work:
SELECT
b.BranchNo AS "Branch Number",
p.PropertyNo || ', ' || p.PostCode || ', ' || p.City || ', ' || p.Street AS "Object address" ,
count(v.ViewDate) as "View count"
FROM Branch b INNER JOIN PropertyForRent p ON b.BranchNo=p.PropertyBranchNo
INNER JOIN Viewing v ON p.PropertyNo=v.ViewPropertyNo WHERE v.ViewDate>='2014-01-01'
GROUP BY b.BranchNo, p.PropertyNo, p.PostCode, p.City, p.Street
HAVING count(v.ViewDate) >= 3;
You need to do grouping in Viewing table sub-query to get the counts. Then join to that.
SELECT b.BranchNo AS [Branch Number],
p.PropertyNo + ', ' + p.PostCode + ', ' + p.City + ', ' + p.Street AS [Object address],
v.ViewCount
FROM Branch b
INNER JOIN PropertyForRent p ON b.BranchNo=p.PropertyBranchNo
INNER JOIN (SELECT ViewPropertyNo, COUNT(*) as ViewCount
FROM Viewing
WHERE v.ViewDate>='2014-01-01'
GROUP BY ViewPropertyNo
) AS v ON p.PropertyNo = v.ViewPropertyNo
WHERE v.ViewCount >= 3;

Using a parameter from another query in a like clause

I am trying to do a join and where column using an inner query. the actual query is a bit more complicated that the below but I have simplified it for this questiion. Consider I have a peopel table. I have in it two likes. The first name is George in both rows but the last_name is Lloyd and Lloyds in each row. I want to be able to return both rows. I am looking for Lloyd - returning that in the inner join and then trying to do a like int he outside query. This only returns the first row though with Lloyd. not the one with the s.
Is it possible to do this? If I was looking for Lloyd and hardcoding it I could do
like 'Lloyd%'
but in my case there are many rows Im returning so I want to do it dynamically.
select * from people p
join
(select p1.id, p1.first_name, p1.last_name
from people p1
where p1.first_name = 'George' and p1.last_name = 'Lloyd' and p1.id = 17)
un on un.id = p.id
where p.first_name = un.first_name and p.last_name like '%' || un.last_name || '%'
Thanks
You are only receiving one row because your query is predicated on just one row (assuming id is unique in your table)
Remove the id = 17
select *
from people
where first_name = :first_name
and last_name like :last_name
You can try this:
SELECT * FROM PEOPLE P
INNER JOIN PEOPLE P2 ON P.FIRST_NAME = P2.FIRST_NAME
and instr(upper(p.last_name),upper(p2.last_name))<>0;
Because your join condition is on un.id = p.id, your result set is always restricted to id = 17.
It sounds like this is the query you were trying to write:
select * from people p
join
(select p1.id, p1.first_name, p1.last_name
from people p1
where p1.first_name = 'George' and p1.last_name = 'Lloyd' and p1.id = 17)
un on p.first_name = un.first_name and p.last_name like '%' || un.last_name || '%'
... which I would personally clean up a little to this:
select p2.*
from people p1
join people p2
on p2.first_name = p1.first_name
and p2.last_name like '%' || p1.last_name || '%'
where p1.first_name = 'George'
and p1.last_name = 'Lloyd'
and p1.id = 17

Postgresql: Subquery in FROM must have an alias - with multiple joins

I get the following error:
ERROR: subquery in FROM must have an alias
LINE 11: (SELECT "domiciles"."id" AS id,
^
HINT: For example, FROM (SELECT ...) [AS] foo.
with the following SQL query:
SELECT "domiciles".*
FROM "domiciles"
LEFT OUTER JOIN
(SELECT "domiciles"."id" AS id,
string_agg("locations"."name"::text, ' ') AS name
FROM "domiciles"
INNER JOIN "locations" ON "locations"."id" = "domiciles"."place_id"
AND "locations"."type" IN ('Place')
GROUP BY "domiciles"."id") place ON place.id = "domiciles"."id"
LEFT OUTER JOIN
(SELECT "domiciles"."id" AS id,
string_agg("accounts"."email"::text, ' ') AS email
FROM "domiciles"
INNER JOIN "accounts" ON "accounts"."id" = "domiciles"."user_id"
AND "accounts"."type" IN ('User')
GROUP BY "domiciles"."id") user ON user.id = "domiciles"."id"
WHERE "domiciles"."deleted_at" IS NULL
I have tried to add AS 'some_text' in many parts of the query, but I cannot solve this problem. Any idea?
Here the complete SQL query to have an idea what I want to do:
SELECT "domiciles".*,
((ts_rank((to_tsvector('german', unaccent(coalesce("domiciles"."is_default"::text, ''))) || to_tsvector('german', unaccent(coalesce("domiciles"."created_at"::text, ''))) || to_tsvector('german', unaccent(coalesce("domiciles"."updated_at"::text, ''))) || to_tsvector('german', unaccent(coalesce(place.name::text, ''))) || to_tsvector('german', unaccent(coalesce(user.email::text, ''))) || to_tsvector('german', unaccent(coalesce(owner.email::text, '')))), (to_tsquery('german', ''' ' || unaccent('abc') || ' ''' || ':*')), 0))) AS pg_search_rank
FROM "domiciles"
LEFT OUTER JOIN
(SELECT "domiciles"."id" AS id,
string_agg("locations"."name"::text, ' ') AS name
FROM "domiciles"
INNER JOIN "locations" ON "locations"."id" = "domiciles"."place_id"
AND "locations"."type" IN ('Place')
AND "locations"."deleted_at" IS NULL
GROUP BY "domiciles"."id") place ON place.id = "domiciles"."id"
LEFT OUTER JOIN
(SELECT "domiciles"."id" AS id,
string_agg("accounts"."email"::text, ' ') AS email
FROM "domiciles"
INNER JOIN "accounts" ON "accounts"."id" = "domiciles"."user_id"
AND "accounts"."type" IN ('User')
AND "accounts"."deleted_at" IS NULL
GROUP BY "domiciles"."id") user ON user.id = "domiciles"."id"
LEFT OUTER JOIN
(SELECT "domiciles"."id" AS id,
string_agg("accounts"."email"::text, ' ') AS email
FROM "domiciles"
INNER JOIN "locations" ON "locations"."id" = "domiciles"."place_id"
AND "locations"."type" IN ('Place')
AND "locations"."deleted_at" IS NULL
INNER JOIN "accounts" ON "accounts"."id" = "locations"."user_id"
AND "accounts"."type" IN ('User')
AND "accounts"."deleted_at" IS NULL
GROUP BY "domiciles"."id") owner ON owner.id = "domiciles"."id"
WHERE "domiciles"."deleted_at" IS NULL
AND "domiciles"."user_id" = $1
AND (((to_tsvector('german', unaccent(coalesce("domiciles"."is_default"::text, ''))) || to_tsvector('german', unaccent(coalesce("domiciles"."created_at"::text, ''))) || to_tsvector('german', unaccent(coalesce("domiciles"."updated_at"::text, ''))) || to_tsvector('german', unaccent(coalesce(place.name::text, ''))) || to_tsvector('german', unaccent(coalesce(user.email::text, ''))) || to_tsvector('german', unaccent(coalesce(owner.email::text, '')))) ## (to_tsquery('german', ''' ' || unaccent('abc') || ' ''' || ':*'))))
Found the problem! The following sentences:
place ON place.id
coalesce(place.name::text, '')
should be
"place" ON "place"."id"
coalesce("place"."name"::text, '')
and this for all sentences: place, owner, user, id