SQL Server 2008, how to join tables on different attributes [duplicate] - sql

This question already has answers here:
How to do an inner join on row number in sql server
(2 answers)
Closed 6 years ago.
I have two tables like this:
Table A:
ID VAL
1 10
2 20
3 30
4 40
5 50
Table B:
ID VAL2
sd 50
gh 80
dv 90
bf 100
ww 45
Joined table:
ID Val VAL2
1 10 50
2 20 100
3 30 45
4 40 80
5 50 90
So, ID in table A is matched to an ID in table B
1 - sd, 2 - bf, 3 - ww, 4 - gh, 5 - dv
How can I join these two?
select A.ID, A.VAL, B.Val
from tableA A
**left join tableB B on A.ID = B.ID ??**
Thank you!

One option here, which does not require the use of a temporary table, would be to create an inline table containing the mappings of table A's ID values to table B's ID values.
SELECT t1.ID AS ID
t1.VAL AS Val,
t3.VAL2 AS Val2
FROM tableA t1
INNER JOIN
(
SELECT 1 AS IDA, 'sd' AS IDB
UNION ALL
SELECT 2 AS IDA, 'bf' AS IDB
UNION ALL
SELECT 3 AS IDA, '22' AS IDB
UNION ALL
SELECT 4 AS IDA, 'gh' AS IDB
UNION ALL
SELECT 5 AS IDA, 'dv' AS IDB
) t2
ON t1.ID = t2.IDA
INNER JOIN tableB t3
ON t2.IDB = t3.ID

SELECT a.ID, a.VAL, B.VAL2 FROM
(
SELECT CASE WHEN ID = 'sd' THEN 1
WHEN ID = 'bf' THEN 2
WHEN ID = 'ww' THEN 3
WHEN ID = 'gh' THEN 4
WHEN ID = 'dv' THEN 5
END AS ID, VAL2 FROM tableB
) AS t INNER JOIN tableA a ON a.ID = t.Id

Related

Sum of recursive tree in postgres

have a tree structure that looks like this:
root
A B
A1 A2 B1 B2
A1.1 A1.2 A2.1 B1.1
the table looks something like this:
id | name |value | parent_id
1 root null null
2 A null 1
3 B null 1
4 A1 null 2
5 A1.1 2 4
6 A1.2 3 4
7 A2 null 2
8 A2.1 5 7
9 B1 null 3
10 B2 1 3
11 B1.1 10 9
.........................
All the non leaf nodes must contain the sum of their children, not only the root node. For example, this is how my desired output looks like:
id | name |value | parent_id
1 root 21 null
2 A 10 1
3 B 11 1
4 A1 5 2
5 A1.1 2 4
6 A1.2 3 4
7 A2 5 2
8 A2.1 5 7
9 B1 10 3
10 B2 1 3
11 B1.1 10 9
how can i achieve this with a fast Postgres query
You need a recursive CTE in the UPDATE statement:
UPDATE tablename AS t
SET "value" = c.value
FROM (
WITH RECURSIVE cte AS(
SELECT t1.id, t1.name, t1.value, t1.parent_id
FROM tablename t1
WHERE NOT EXISTS (SELECT 1 FROM tablename t2 WHERE t2.parent_id = t1.id)
UNION ALL
SELECT t.id, t.name, c.value, t.parent_id
FROM tablename t INNER JOIN cte c
ON c.parent_id = t.id
)
SELECT id, SUM("value") "value"
FROM cte
GROUP BY id
) c
WHERE c.id = t.id;
See the demo.
Using a recursive cte:
with recursive cte(id, name, val, p) as (
select t.id, t.name, t.value, '.'||(select t1.id from tbl t1 where t1.parent_id is null)||'.'||t.id||'.' from tbl t where t.parent_id = (select t1.id from tbl t1 where t1.parent_id is null)
union all
select t.id, t.name, t.value, c.p||t.id||'.' from cte c join tbl t on c.id = t.parent_id
)
select t.id, sum(case when c.val is null then 0 else c.val end) from tbl t
join cte c on c.p like '%.'||t.id||'.%' group by t.id order by t.id
To update your original table with the summed child node values, you can use a subquery in update:
update tbl set value = t1.val from (
select t.id tid, sum(case when c.val is null then 0 else c.val end) val from tbl t
join cte c on c.p like '%.'||t.id||'.%' group by t.id) t1
where t1.tid = id

SQL only join when key matches one criteria

I have two tables that look like the following:
Table 1 (IDs):
ID
1
2
3
4
5
Table 2 (Categories):
ID CAT
1 A
1 B
2 A
3 A
4 B
5 A
5 B
5 C
What I would like to do is join table 2 to table 1 and only keep instances where there is 1) Only one match 2) That match is CAT = A
So my final table would look like:
ID CAT
2 A
3 A
One method is aggregation and having:
select id, max(cat)
from t
group by id
having count(*) = 1 and
max(cat) = 'A';
You can use a subquery with not exists:
select t1.id, t2.cat
from table1 t1
join table2 t2 on t1.id = t2.id
where t2.cat = 'A' and not exists (select 1 from table2 t3 where t3.id = t1.id and t3.cat != 'A');
Output:
id
cat
2
A
3
A

Query to Group by multiple table data in SQL Server

I have 3 tables containing data as below and I want to group data into single output.
table1
pid val
1 1
2 2
table2
id pid val
1 1 1
2 1 2
3 2 1
table3
id pid val
1 1 1
2 1 2
3 2 1
4 2 1
Required Output:
pid output
1 1 (table1 val + table2 Sum(val) - table3 Sum(val))
2 1 (table1 val + table2 Sum(val) - table3 Sum(val))
Please help with optimal query which can achieve this without any temp table.
Assume that pid is primary key of table1, you could use this
SELECT
t1.pid,
t1.val + t2.sumval - t3.sumval
FROM
table1 t1
INNER JOIN
(SELECT
pid, SUM(val) as sumval
FROM
table2
GROUP BY
pid) t2
ON
t2.pid = t1.pid
INNER JOIN
(SELECT
pid, SUM(val) as sumval
FROM
table3
GROUP BY
pid) t3
ON
t3.pid = t1.pid;

require to form a sql query

I was working on preparing a query where I was stuck.
Consider tables below:
table1
id key col1
-- --- -----
1 1 abc
2 2 d
3 3 s
4 4 xyz
table2
id col1 foreignkey
-- ---- ----------
1 12 1
2 13 1
3 14 1
4 12 2
5 13 2
Now what I need is to select only those records from table1 for which the corresponding entries in table2 does not have say col1 value as 12.
So the challenge is after applying join even though it will skip for value 1 corresponding to col1 equal to 12 it still has another multiple rows whose values are say 13, 14 for which also they have same foreignkey. Now what I want is if there is a single row having value 12 then it should not pick that id at all from table1.
How can I form a query with this?
The output which i need is say from above table structure i want to get those records from table1 for which col1 value from table2 does not have value as 14.
so my query should return me only row 2 from table1 and not row 1.
Another way of doing that. The first two queries are just for making the sample data.
;WITH t1(id ,[key] ,col1) AS
(
SELECT 1 , 1 , 'abc' UNION ALL
SELECT 2 , 2 , 'd' UNION ALL
SELECT 3 , 3 , 's' UNION ALL
SELECT 4 , 4 , 'xyz'
)
,t2(id ,col1, foreignkey) AS
(
SELECT 1 , 12 , 1 UNION ALL
SELECT 2 , 13 , 1 UNION ALL
SELECT 3 , 14 , 1 UNION ALL
SELECT 4 ,12 , 2 UNION ALL
SELECT 5 ,13 , 2
)
SELECT id, [key], col1
FROM t1
WHERE id NOT IN (SELECT t2.Id
FROM t2
INNER JOIN t1 ON t1.Id = t2.foreignkey
WHERE t2.col1 = 14)
This is a typical case for NOT EXISTS:
SELECT id, [key], col1
FROM table1 t1
WHERE NOT EXISTS (SELECT 1
FROM table2 t2
WHERE t2.foreignkey = t1.id AND t2.col1 = 14)
The above query will not select a row from table1 if there is a single correlated row in table2 having col1 = 14.
Output:
id key col1
-------------
2 2 d
3 3 s
4 4 xyz
If you want to return records that, in addition to the criterion set above, also have correlated records in table2, then you can use the following query:
SELECT t1.id, MAX(t1.[key]) AS [key], MAX(t1.col1) AS col1
FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.foreignkey
GROUP BY t1.id
HAVING COUNT(CASE WHEN t2.col1 = 14 THEN 1 END) = 0
Output:
id key col1
-------------
2 2 d
You can also achieve the same result with the second query using a combination of EXISTS and NOT EXISTS:
SELECT id, [key], col1
FROM table1 t1
WHERE EXISTS (SELECT 1
FROM table2 t2
WHERE t2.foreignkey = t1.id)
AND
NOT EXISTS (SELECT 1
FROM table2 t3
WHERE t3.foreignkey = t1.id AND t3.col1 = 14)
select t1.id,t1.key,
(select ROW_NUMBER() OVER(PARTITION BY col1 ORDER BY col1 DESC) AS Row,* into
#Temp from table1)
from table1 t1
inner join table2 t2 on t1.id=t2.foreignkey
where t2.col1=(select col1 from #temp where row>1)

Joining two tables in a select

I have two tables:
TABLE 1
ID VALUE
1 ABC
2 DEF
3 GHI
4 JKL
5 XYZ
TABLE 2
ID T1_ID VALUE
1 1 A
2 1 B
3 2 A
4 3 A
5 3 B
6 4 B
I want to select all rows from TABLE 1 which have a TABLE 2 row for Values A AND B.
This would be rows 1 and 3 (not 2 because it has only A, not 4 because it has only B).
Can I do this without a subquery?
(Note: I also need to query against values in table 1 so I can't just query table 2.)
Tadaaah! Without a subquery.
select distinct
t1.*
from
Table1 t1
inner join Table2 t2a on t2a.t1_ID = t1.ID and t2a.VALUE = 'A'
inner join Table2 t2b on t2b.t1_ID = t1.ID and t2b.VALUE = 'B'
SELECT t1.ID,
t1.VALUE
FROM Table1 t1
JOIN Table2 t2
ON t1.ID = t2.T1_ID
WHERE t2.VALUE IN ( 'A', 'B' )
GROUP BY t1.ID,
t1.VALUE
HAVING COUNT(DISTINCT t2.VALUE) = 2