SQL Query subquery in select - sql

I have 2 tables.
Table1
+----+------+
| Id | Name |
+----+------+
| | |
+----+------+
Table2
+-----+-----------+------+-------+---------+
| Id | Table1_ID | Name | Value | Created |
+-----+-----------+------+-------+---------+
| | | | | |
+-----+-----------+------+-------+---------+
When I run a SELECT * FROM Table2, I want the Table1_ID to be replaced with the name of that item ID from Table 1, rather than the ID. How can I do that?

User Inner join, Like this
SELECT
T2.Id
T1_Name = T1.Name ,--Table1_ID
T2_Name = T2.Name
T2.Value
T2.Created
FROM Table1 T1
INNER JOIN Table2 T2
ON T1.ID = T2.Table1_ID

You can use INNER JOIN for that.
INNER JOIN Syntax 1
SELECT *
FROM table1
INNER JOIN table2 ON table1.id = table2.fk_id
INNER JOIN Syntax 2
SELECT *
FROM table1
INNER JOIN table2
WHERE table1.id = table2.fk_id
  
SELECT Table2.Id, Table2.Name, Table1.Name, Table2.Value, Table2.Created
FROM Table2
INNER JOIN Table1 ON Table1.ID = Table2.Table1_ID
Recommended Readings
http://sql.sh/cours/jointures/inner-join
https://www.w3schools.com/sql/sql_join_inner
https://www.tutorialspoint.com/sql/sql-inner-joins

Related

How to select from joined table with WHERE clause only when value exist?

I want to SELECT one record from table1 (WHERE t1.id = 1) and then JOIN table2 and table3 (t2.field2 and t3.field3) to table1 but ONLY if the values exists (IS NOT NULL).
So for example, if the value doesn't exist for t3.field3, the field3 column is not displayed for that table...
t1
id | field1
---------------
1 | f1val
2 | f1val
3 | f1val
t2
id(fk) | field2
-------------------
1 | f2val
2 | null
3 | null
t3
id(fk) | field3
-------------------
1 | null
2 | f3val
3 | f3val
the code I tried to do is this:
SELECT t1.id, t2.field1, t3.field3
FROM (
SELECT t1.id
FROM t1
WHERE t1.id = 1
)
LEFT JOIN t2 ON t2.id = t1.id AND t2.id is not null
LEFT JOIN t3 ON t2.id = t1.id AND t3.id is not null;
The joined table returned from the query above looks like this:
id | field2 | field3
----------------------------
1 | f1val | null
However, since field3 is null, I want it to return only the id and field2 like this:
id | field2
----------------
1 | f1val
Your help will be highly appreciated.
You could return one column, using coalesce():
SELECT t1.id, COALESCE(t2.field1, t3.field3) as field_2_3
FROM t1 LEFT JOIN
t2
ON t2.id = t1.id LEFT JOIN
t3
ON t3.id = t1.id
WHERE t1.id = 1;
However, you cannot sometimes return two columns and sometimes return 3 columns.
Notes:
The subquery on t1 is utterly unnecessary. You can just apply the filter in a single WHERE clause.
The comparisons for IS NOT NULL are unnecessary because they fail the JOIN condition anyway.
The last JOIN condition is presumably on t3.id = t1.id.

SQL - Finding optional value in another table

In the scenario where there are two tables, one column in the first has a nullable key to another table.
table1_id | table1_key | table2_id | table2_value
----------+------------+-----------+--------------
1 | 1 | 1 | 3
2 | | |
3 | 3 | 3 | 1
4 | 1 | 1 | 3
With a single efficient statement, I want to get all rows from table1 and data from table2 if they exist.
My current method does a union between two statements.
SELECT
table1.id as table1_id,
table1.fkey as table1_key,
table2.id as table2_id,
table2.value as table_value
FROM
table1,
table2
WHERE
table1.fkey = table2.id
UNION
SELECT
table1.id as table1_id,
null,
null,
null
FROM
table1,
table2
WHERE
table1.fkey = NOT IN (SELECT id FROM table2)
How can this be done more efficiently in a single select statement?
A left join would do the job,
SELECT
table1.id as table1_id,
table1.fkey as table1_key,
table2.id as table2_id,
table2.value as table_value
FROM table1
LEFT OUTER JOIN table2
ON table1.fkey = table2.id
You need a join between table1 and table2 on the foreign key relationship.
From your question, I understand that column fkey in table1 is a foreign key to column id in table2.
You want to retrieve rows from table1 even if there is no matching row in table2. Hence you need a left outer join
select t1.id as t1_id
,t1.fkey as t2_id
,t2.value as t2_value
from table1 t1
left outer join table2 t2
on t1.fkey = t2.id

Checking if a set of values exists in multiple tables

I'd like to know better ways of checking if a set of values is a subset of another.
For some reason, I can't make IN work so I use something like this approach:
-- check if `table1.id` is in other tables
SELECT (
-- check if all `table1.id` is in table2's `table1_id`
ARRAY(SELECT id FROM table1) <# ARRAY(SELECT table1_id FROM table2)
AND
-- check if all `table1.id` is in table3's `table1_id`
ARRAY(SELECT id FROM table1) <# ARRAY(SELECT table1_id FROM table3)
-- ...and so on
)
So for example, if I have these these two rows on table1:
+----+
| id |
+----+
| 1 |
| 2 |
+----+
And these two rows on table2:
+----+-----------+
| id | table1_id |
+----+-----------+
| 1 | 1 |
| 2 | 2 |
+----+-----------+
And this one row at table3:
+----+-----------+
| id | table1_id |
+----+-----------+
| 1 | 2 |
+----+-----------+
The result would be false because table3 does not contain both line_id of 1 and 2.
But, if table3 is like below:
+----+-----------+
| id | table1_id |
+----+-----------+
| 1 | 2 |
| 2 | 1 |
+----+-----------+
It would return true
Is my approach already good? If I use IN correctly, would it be faster? Are there some other ways that I am totally missing?
You can just use inner joins and count the results:
with table1_count as (
select count(*) as count
FROM table1
),
all_table_count as (
select count(*) as count
from (
select table1.id from table1
join table2 on table1.id = table2.table1_id
join table3 on table1.id = table3.table1_id
) sub
)
select table1_count.count = all_table_count.count as ids_everywhere
from all_table_count,table1_count
;
ids_everywhere
----------------
f
(1 row)
Joining will be much faster than array comparison.
Use exists
select t1.*
from Table1 t1
where exists (select 1 from table2 t2 where t2.table1_id = t1.id)
and exists (select 1 from table3 t3 where t3.table1_id = t1.id)
and exists (select 1 from table4 t4 where t4.table1_id = t1.id)
You can also use exists in a case statement
select t1.id,
case
when exists (select 1 from table2 t2 where t2.table1_id = t1.id)
and exists (select 1 from table3 t3 where t3.table1_id = t1.id)
and exists (select 1 from table4 t4 where t4.table1_id = t1.id)
then 1
else 0
end
from Table1 t1
Or list each separate (edit):
select t1.id,
case
when exists (select 1 from table2 t2 where t2.table1_id = t1.id)
then 1 else 0
end as in_tab2,
case
when exists (select 1 from table3 t3 where t3.table1_id = t1.id)
then 1 else 0
end as in_tab3,
case
when exists (select 1 from table4 t4 where t4.table1_id = t1.id)
then 1 else 0
end as in_tab4
from table1

Joining two sql tables with a one to many relationship, but want the max of the second table

I am trying to join two tables one is a unique feature the seconds is readings taken on several dates that relate to the unique features. I want all of the records in the first table plus the most recent reading. I was able to get the results I was looking for before adding the shape field. By using the code
SELECT
Table1.Name, Table1.ID, Table1.Shape,
Max(Table2.DATE) as Date
FROM
Table1
LEFT OUTER JOIN
Table2 ON Table1.ID = table2.ID
GROUP BY
Table1.Name, Table1.ID, Table1.Shape
The shape field is a geometry type and I get the error
'The type "Geometry" is not comparable. It can not be use in the Group By Clause'
So I need to go about it a different way, but not sure how.
Below is a sample of the two tables and the desired results.
Table1
Name| ID |Shape
AA1 | 1 | X
BA2 | 2 | Y
CA1 | 3 | Z
CA2 | 4 | Q
Table2
ID | Date
1 | 5/27/2013
1 | 6/27/2014
2 | 5/27/2013
2 | 6/27/2014
3 | 5/27/2013
3 | 6/27/2014
My Desired Result is
Name| ID |Shape |Date
AA1 | 1 | X | 6/27/2014
BA2 | 2 | Y | 6/27/2014
CA1 | 3 | Z | 6/27/2014
CA2 | 4 | Q | Null
You can do the aggregation on Table2 in a CTE, finding the MAX(DATE) for each ID, and then join that result to Table1:
WITH AggregatedTable2(ID, MaxDate) AS
(
SELECT
ID, MAX(DATE)
FROM
Table2
GROUP BY
ID
)
SELECT
t1.ID, t1.Name, t1.Shape, t2.MaxDate
FROM
Table1 t1
LEFT JOIN
AggregatedTable2 t2 ON t1.ID = t2.ID
Try casting geometry as a varchar.
Select Table1.Name, Table1.ID, cast(Table1.Shape as varchar(1)) AS Shape, Max(Table2.DATE) as Date
FROM Table1 LEFT OUTER JOIN
Table2 ON Table1.ID = table2.ID
Group By Table1.Name, Table1.ID, cast(Table1.Shape as varchar(1))
Try this:
SELECT t1.Name
, t1.ID
, t1.Shape
, MAX(t2.Date) As Date
FROM Table1 AS t1
LEFT JOIN Table2 AS t2
ON t2.ID = t1.ID
GROUP
BY t1.Name
, t1.ID
, t1.Shape

Postgresql array_agg, INNER JOIN and LEFT JOIN problems

I have a slight problem with one of my query. The goal of this query is to get all the table1 items of a user and their information. As you can see, the data model is quite complex (for good reasons), and this requires an big query (my goal is to gather everything with one query only).
Here is the data model :
What I want :
All T1 info
All T2 info for one T1 item (it is a 1 to n relations, so I'll use array_agg)
All T3 info for one T1 item
All T4 info for one T1 item
All T6 info for one T1 item
i18n info for the T1 itemp
Here are the table1_table2 and table4_table6 SELECT * :
table1_id | table2_id
-------------+---------------
item2id | table2item1
item4id | table2item2
item4id | table2item1
item5id | table2item3
item5id | table2item2
table4_id | table6_id
------------------+--------------------
table4item1 | table6item1
table4item1 | table6item2
table4item2 | table6item2
table4item3 | table6item3
table4item1 | table6item3
table4item2 | table6item3
Here are the Table1 SELECT with id and its foreign key.
table1_id | table3_id
------------------------
item1id | table3item1
item2id | table3item1
item6id | table3item4
item3id | table3item2
item4id | table3item2
item5id | table3item3
Same for table3 :
table3_id | table4_id
------------+--------------
table3item1 | table4item1
table3item4 | table4item1
table3item2 | table4item2
table3item3 | table4item3
Finally, here is my query :
SELECT t1.id,
na.name,
array_to_json(array_agg(row_to_json(t2))) AS table2items,
array_to_json(array_agg(row_to_json(t6))) AS table6items
FROM table1 t1
INNER JOIN table1_i18n na ON na.table1_id = t1.id
INNER JOIN table3 t3 ON t3.id = t1.table3_id
INNER JOIN table4 t4 ON t4.id = t3.table4_id
LEFT JOIN table1_table2 t1t2 ON t1t2.table1_id = t1.id
LEFT JOIN table2 t2 ON t2.id = t1t2.table2_id
LEFT JOIN table4_table6 t5_t6 ON t5_t6.table5_id = t3.table4_id
LEFT JOIN table6 t6 ON t6.id = t5_t6.table6_id
WHERE t1.user_id = 'myuserid' AND na.lang = 'en_US'
GROUP BY t1.id, na.name, t4.id
ORDER BY t1.id;
Here is the result :
id | name | table3_id | table4_id | table2items | table6items
-------------+------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
item1id | MyFirstItem | table3item1 | table4item1 | [null,null,null] | [{"id":"table6item1"},{"id":"table6item2"},{"id":"table6item3"}]
item2id | MySecondItem | table3item1 | table4item1 | [{"table2item1","data1":"damage","data2":10},{"id":"table2item1","data1":"damage","data2":10},{"id":"table2item1","data1":"damage","data2":10}] | [{"id":"table6item1"},{"id":"table6item2"},{"id":"table6item3"}]
item3id | MyThirdItem | table3item2 | table4item2 | [null,null] | [{"id":"table6item2"},{"id":"table6item3"}]
item4id | MyFourthItem | table3item2 | table4item2 | [{"id":"table2item2","data1":"range","data2":20},{"id":"table2item1","data1":"damage","data2":10},{"id":"table2item2","data1":"range","data2":20},{"id":"table2item1","data1":"damage","data2":10}] | [{"id":"table6item2"},{"id":"table6item3"},{"id":"table6item3"},{"id":"table6item2"}]
item5id | MyFifthItem | table3item3 | table4item3 | [{"id":"table2item3","data1":"range","data2":20},{"id":"table2item2","data1":"range","data2":20}] | [{"id":"table6item3"},{"id":"table6item3"}]
item6id | MySixthItem | table3item4 | table4item1 | [null,null,null] | [{"id":"table6item2"},{"id":"table6item1"},{"id":"table6item3"}]
Well, I've got a problem here. As you can see, my table2_items and table6_items arrays have the same size. I don't know the reason for this, but it seems that I'm missing something.
Worse, instead of filling this array with null value, this query creates duplicates which should not appear.
Details :
item1 and item6 have the same problem : no links to table2, and 3 items in table6. I end up with an array [null, null, null] for table2_items
item2 has 3 links to table 6, and 1 to table2. I end up with 3 times the same table2 object in the array
item4... I don't know what's happening here. Should have 2 things in each array, and I've got 4 (duplicates)
item5 : you can clearly see the duplication.
I have tried to group by table6.id, or table2.id. It doesn't work (I have got a line for each of them, so several line for each item).
Note : If I do
SELECT t1.id,
na.name,
array_to_json(array_agg(row_to_json(t2))) AS table2items,
FROM table1 t1
INNER JOIN table1_i18n na ON na.table1_id = t1.id
INNER JOIN table3 t3 ON t3.id = t1.table3_id
INNER JOIN table4 t4 ON t4.id = t3.table4_id
LEFT JOIN table1_table2 t1t2 ON t1t2.table1_id = t1.id
LEFT JOIN table2 t2 ON t2.id = t1t2.table2_id
WHERE t1.user_id = 'myuserid' AND na.lang = 'en_US'
GROUP BY t1.id, na.name, t4.id
ORDER BY t1.id;
alone, it works perfectly. Same for t6. It's only when I try to gather everything at the same time that I got some problems.
If it is not clear enough, ask for details. It's really not easy to explain such a problem :).