select from multiple tables, when one is never used - sql

I improved my question with example tables for a better understanding
I have 3 tables with following rows:
TABLE1 t1 TABLE t2 TABLE t3
ID NAME OBS ID HW_VER ID SERIAL
----------------- ----------- ------------
1 | Name1 | Obs1 1 | HWVer1 5 | Serial5
2 | Name2 | Obs2 2 | HWVer2 6 | Serial6
3 | Name3 | Obs3 3 | HWVer3 7 | Serial7
4 | Name4 | Obs4
5 | Name5 | Obs5
6 | Name6 | Obs6
7 | Name7 | Obs7
Now, I want to select the id, name and obs when 2 conditions are fulfilled:
the id is present in t2 or t3 (never in both);
it refers to either t2 or t3 attributes (eg. t2.HW_VER='HWVER1'), never on both
I did something like this, but it's wrong:
SELECT DISTINCT t1.id, t1.name, t1.obs
FROM table1 t1, table2 t2, table3 t3
WHERE t1.id IN (t2.id, t3.id) AND t3.serial='Serial6';
I cannot use unions, external tables or views for this.
Please let me know in case of further questions.
Thanks a lot for your answers, I really appreciate your time..

You need to select from T2 OR T3 but never both? I think you want something like this
select count(*)
from t1
where exists (
select 'x'
from t2
where MyPrimaryKey_Name = 'random_name'
and t2.id = t1.id
)
or exists (
select 'x'
from t3
where MyPrimaryKey_Name = 'random_name'
and t3.id = t1.id
)

Related

How to do an outer join with full result between two tables

I have two tables:
TABLE1
id_attr
-------
1
2
3
TABLE2
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
As a result I want a table that show:
RESULT
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
So I want the row with id=10 and id_attr=3 also when id_Attr=3 is missing in TABLE2 (and I know that because I have a NULL value (or something else) in the val column of RESULT.
NB: I could have others ids in table2. For example, after insert this row on table2: {11,1,A}, as RESULT I want:
id | id_attr | val
----------------------
10 | 1 | A
10 | 2 | B
10 | 3 | NULL
11 | 1 | A
11 | 2 | NULL
11 | 3 | NULL
So, for every id, I want always the match with all id_attr.
Your specific example only has one id, so you can use the following:
select t2.id, t2.id_attr, t2.val
from table2 t2
union all
select 10, t1.id_attr, NULL
from table1 t1
where not exists (select 1 from table2 t2 where t2.id_attr = t1.id_attr);
EDIT:
You can get all combinations of attributes and ids in the following way. Use a cross join to create all the rows you want and then a left join to bring in the data you want:
select i.id, t1.id_attr, t2.val
from (select distinct id from table2) i cross join
table1 t1 left join
table2 t2
on t2.id = i.id and t2.id_attr = t1.id_attr;
It sounds like you want to do just an outer join on id_attr instead of id.
select * from table2 t2
left outer join table1 t1 on t2.id_attr = t1.id_attr;

What is the correct way from performance perspective to match(replace) every value in every row in temp table using SQL Server 2016 or 2017?

I am wondering what should I use in SQL Server 2016 or 2017 (CTE, LOOP, JOINS, CURSOR, REPLACE, etc) to match (replace) every value in every row in temp table? What is the best solution from performance perspective?
Source Table
|id |id2|
| 1 | 2 |
| 2 | 1 |
| 1 | 1 |
| 2 | 2 |
Mapping Table
|id |newid|
| 1 | 3 |
| 2 | 4 |
Expected result
|id |id2|
| 3 | 4 |
| 4 | 3 |
| 3 | 3 |
| 4 | 4 |
You may join the second table to the first table twice:
WITH cte AS (
SELECT
t1.id AS id_old,
t1.id2 AS id2_old,
t2a.newid AS id_new,
t2b.newid AS id2_new
FROM table1 t1
LEFT JOIN table2 t2a
ON t1.id = t2a.id
LEFT JOIN table2 t2b
ON t1.id2 = t2b.id
)
UPDATE cte
SET
id_old = id_new,
id2_old = id2_new;
Demo
Not sure if you want just a select here, or maybe an update, or an insert into another table. In any case, the core logic I gave above should work for all these cases.
You'd need to apply joins on update query. Something like this:
Update tblA set column1 = 'something', column2 = 'something'
from actualName tblA
inner join MappingTable tblB
on tblA.ID = tblB.ID
this query will compare eachrow with ids and if matched then it will update/replace the value of the column as you desire. :)
Do the self join only
SELECT t1.id2 as id, t2.id2
FROM table1 t
INNER JOIN table2 t1 on t1.id = t.id
INNER JOIN table2 t2 on t2.id = t.id2
This may have best performance from solutions posted here if you have indexes set appropriately:
select (select [newid] from MappingTable where id = [ST].[id]) [id],
(select [newid] from MappingTable where id = [ST].[id2]) [id2]
from SourecTable [ST]

Differentiate null and empty in Postgres JOIN query

If I have tables like this:
t1:
id | name
----+------
1 | a
2 | b
3 | c
4 | d
t2:
id | value
----+-------
10 | xxx
20 | yyy
30 | zzz
t_join:
t1_id | t2_id
-------+-------
1 | 10
2 | 20
3 | 30
A SELECT query for t1.id=1 looks like:
SELECT t1.id, t1.name, t2.value FROM t1, t2,t_join WHERE t1.id=t_join.t1_id AND t2.id=t_join.t2_id AND t1.id=1;
And of course returns:
id | name | value
----+------+-------
1 | a | xxx
If I do the same thing with id=4, I get nothing.
SELECT t1.id, t1.name, t2.value FROM t1, t2,t_join WHERE t1.id=t_join.t1_id AND t2.id=t_join.t2_id AND t1.id=4;
id | name | value
----+------+-------
(0 rows)
And, if I do the same thing with a nonsense id=1234132, I also get nothing.
SELECT t1.id, t1.name, t2.value FROM t1, t2,t_join WHERE t1.id=t_join.t1_id AND t2.id=t_join.t2_id AND t1.id=1234132;
id | name | value
----+------+-------
(0 rows)
Is there a way I can differentiate between having an empty result (id=4) vs something that's null (id=1234132)? I guess I want verification that the id I'm checking exists without a separate query. Is this possible?
How about a left join:
SELECT t1.id, t1.name, t2.value
FROM t1 LEFT JOIN
t_join
ON t1.id = t_join.t1_id LEFT JOIN
t2
ON t2.id = t_join.t2_id
WHERE t1.id = 1;
If t.id is not found, you'll get no rows. If there are no matches in t2, then you'll get a NULL value.
Also, a simple rule: Never use commas in the FROM clause. Always, always use proper, explicit JOIN syntax.

how can i do the following query with Oracle SQL?

------------------
| **table 1** |
------------------
| 1 | 400 |
| 2 | 220 |
| 3 | 123 |
------------------
| **table 2** |
------------------
| 1 | 100 |
formula : table1 - table2 where table1.id=table2.id
------------------
| **Result** |
------------------
| 1 | 300 |
| 2 | 220 |
| 3 | 123 |
You want an outer join to get all rows from table_1 and the matching ones from table2
select t1.id, t1.val - coalesce(t2.val, 0) as result
from table_1 t1
left join table_2 t2 on t1.id = t2.id;
The coalesce(t2.val, 0) is necessary because the outer join will return null for those rows where no id exists in table_2 but t1.val - null would yield null
select t1.id,
nvl2(t2.val,t1.val-t2.val,t1.val) val
from t1,t2
where t1.id=t2.id(+)
order by t1.id;
Try this
select t1.col1, t1.col2-t2.col1 as balance from
table1 t1 left join table2 t2 on t1.col1=t2.col1
I don't the syntax in Oracle sql, but I can give the solution in mysql.
Consider the table with 2 columns:
id , value
SELECT table1.id, table1.value - table2.value
FROM table1, table2
WHERE table1.id=table2.id
OR
SELECT table1.id, table1.value
FROM table1, table2
WHERE NOT (table1.id =table2.id)
In some cases using scalar subquery caching could give better performance. It is on developer to compare execution plans and decide which query is the most appropriate.
with t1 (id, num) as
(
select 1, 400 from dual union all
select 2, 220 from dual union all
select 3, 123 from dual
),
t2(id, num) as
(
select 1, 100 from dual
)
select id,
num - nvl((select num from t2 where t2.id = t1.id), 0) result
from t1;
This is just to show you a different technique for solving problems in which you try to get data from several tables, but some may not have matching rows.
Using outer join in this case is in my opinion more logical.

Is there a way to have one to many + one to many query in one result in group by?

I know the title is confusing. I have an example below. I know how to do it with inline SELECTs, try to avoid that:
T1
id | title
1 a
T2
t1_id | title
1 a1
1 a2
1 a3
T3
t1_id | amount
1 10
The result set should be: t1.id, group_concat(t2.title) , sum(t3.amount)
1 | a1,a2,a3 | 10
How about this?
select t2.t1_id, group_concat(t2.title), t3.amount
from t2
join t3 on t2.t1_id = t3.t1_id
group by t2.t1_id, t3.amount