Join with dynamic Table and Column names - sql

I'm trying to join from a table where the tables and fields are defined within the data instead of keys. So here is what I have
Table Root:
ID | Table | Field
---+---------+-----------
1 | Tab1 | Field1
2 | Tab2 | Field2
3 | Tab1 | Field2
4 | Tab3 | Field4
5 | Tab1 | Field1
Tab1
ID | Field1
---+---------
1 | A
2 | B
3 | C
4 | D
Tab2
ID | Field1 |Field2
---+--------+-----------
1 | X | Bla
2 | Y | 123
3 | Z | 456
Tab3 does not exist
I'd like to have a result like that one:
ID | Value
---+---------
1 | A
2 | 123
3 | NULL -- Field does not match
4 | NULL -- Tables does not exist
5 | NULL -- ID does not exist
Basicly trying to join using the the ID trageting a dynamic table and field.
My Starting Point is somehwere around Here, but this is just for a single specific table. I can't figure out how to join dynamicly or if it even possible without dynamic sql like exec.

you could solve this with a case expression and subqueries, like this example
declare #root table (id int, [table] varchar(10), Field varchar(10))
declare #tab1 table (id int, Field1 varchar(10))
declare #tab2 table (id int, Field1 varchar(10), Field2 varchar(10))
insert into #root (id, [table], Field)
values (1, 'Tab1', 'Field1'), (2, 'Tab2', 'Field2'), (3, 'Tab1', 'Field2'), (4, 'Tab3', 'Field4'), (5, 'Tab1', 'Field1')
insert into #tab1 (id, Field1)
values (1, 'A'), (2, 'B'), (3, 'C'), (4, 'D')
insert into #tab2 (id, Field1, Field2)
values (1, 'X', 'Bla'), (2, 'Y', '123'), (3, 'Z', '456')
select r.id,
case when r.[Table] = 'Tab1' and r.Field = 'Field1' then (select t1.Field1 from #tab1 t1 where t1.ID = r.ID)
when r.[Table] = 'Tab2' and r.Field = 'Field1' then (select t2.Field1 from #tab2 t2 where t2.id = r.id)
when r.[Table] = 'Tab2' and r.Field = 'Field2' then (select t2.Field2 from #tab2 t2 where t2.id = r.id)
end as Value
from #root r
the result is
id Value
-- -------
1 A
2 123
3 null
4 null
5 null

Related

Remove duplicate value in different categories in same table SQL but keep the first category value

Let's say I have a table with id and category like the table below
D_id | D_category
-----------------
1 | A
2 | A
3 | A
1 | B
2 | B
4 | B
5 | B
1 | C
2 | C
4 | C
5 | C
6 | C
Hence the rules are like this
values in category A should not be appear in category B and category C
values in category B should not be appear in category C
The end result should be like this
D_id | D_category
-----------------
1 | A
2 | A
3 | A
4 | B
5 | B
6 | C
I will provide a solution that works but its not an ideal solution can anyone help me to provide a better solution in case there are more categories meaning that if there are more category then it should follow the rules the values in previous categories should not appear in any other categories
DECLARE #A TABLE(
D_id INT NOT NULL,
D_category VARCHAR(MAX));
INSERT INTO #A(D_id,D_category)
VALUES (1, 'A'),
(2, 'A'),
(3, 'A'),
(1, 'B'),
(2, 'B'),
(4, 'B'),
(5, 'B'),
(1, 'C'),
(2, 'C'),
(4, 'C'),
(5, 'C'),
(6, 'C')
DELETE t
FROM #A t
WHERE t.D_category = 'B' AND EXISTS (SELECT 1 FROM #A t2 WHERE t2.D_category = 'A' and t.D_id = t2.D_id)
DELETE t
FROM #A t
WHERE t.D_category = 'C' AND EXISTS (SELECT 1 FROM #A t2 WHERE t2.D_category = 'B' and t.D_id = t2.D_id)
DELETE t
FROM #A t
WHERE t.D_category = 'C' AND EXISTS (SELECT 1 FROM #A t2 WHERE t2.D_category = 'A' and t.D_id = t2.D_id)
select * from #A
Just check that the specified record doesn't exist earlier in the sequence.
select *
from #A A1
where not exists (
select 1
from #A A2
where A2.D_id = A1.D_id
and A2.D_category < A1.D_category
)
or just make use of row_number()
select *
from
(
select *, r = row_number() over (partition by D_id order by D_category)
from #A
) a
where a.r = 1
Delete using the join syntax:
delete a
from my_table a
join my_table b on a.D_id = b.D_id
and a.D_category > b.D_category
See live demo.

Combining select and select union in SQL Server

I have 2 tables
Table A with 3 columns : ID, NAME, VALUE 1
Table B with 3 columns : ID, NAME, VALUE 2
Table A :
ID : A1, A2
NAME: AAA, BBB
VALUE 1 : 1000,2000
Table B :
ID : B1, B2
NAME: CCC,DDD
VALUE 1 : 3000,4000
And I want to show the result like this :
ID, NAME, VALUE 1, VALUE 2
A1 AAA 1000
A2 BBB 2000
A3 CCC 3000
A4 DDD 4000
I have tried union and it works for id, name column. What about regular select + union select, is it possible ?
You can use union all:
select id, name, value1, null as value2
from a
union all
select id, name, null as value1, value2
from b;
online demonstration
MS SQL Server 2017 Schema Setup:
CREATE TABLE Table1
([ID] varchar(2), [NAME] varchar(3), [VALUE1] int)
;
INSERT INTO Table1
([ID], [NAME], [VALUE1])
VALUES
('A1', 'AAA', 1000),
('A2', 'BBB', 2000)
;
CREATE TABLE Table2
([ID] varchar(2), [NAME] varchar(3), [VALUE2] int)
;
INSERT INTO Table2
([ID], [NAME], [VALUE2])
VALUES
('B1', 'CCC', 3000),
('B2', 'DDD', 4000)
;
Query 1:
select id, name, value1, null as value2
from table1
union all
select id, name, null as value1, value2
from table2
Results:
| id | name | value1 | value2 |
|----|------|--------|--------|
| A1 | AAA | 1000 | (null) |
| A2 | BBB | 2000 | (null) |
| B1 | CCC | (null) | 3000 |
| B2 | DDD | (null) | 4000 |
You can also use Full Outer Join
SELECT a.id, a.name, a.value1, b.value2
FROM tableA as a
FULL OUTER JOIN tableB AS b ON
a.id = b.id

sql data where not exists in a table and not duplicate

its a bit tricky please focus on my requirments, I have 2 tables , I want to get data from first table where not exists in the second AND the data in first column are not duplicate for sub id and child id.
example: I have this table
tab1
id subid childid
1 11 77
2 22 55
3 33 66
4 11 77
7 22 55
8 33 60
9 99 98
10 33 60
11 97 98
tab2
id
1
4
7
10
the first thing I want is the id in tab1 doesnt exists in tab2 which will be
2,3,8,9,11 however some of those id have duplicate subid and chilid so i have to exclude them therefore I should have id 3, 9, 11
I tried this query but it returne me also 3 ,9 ,11, 8 , I dont want 8 how to fix the query ?
select *
from tab1 a
where not exists (select 1 from tab2 b where a.id = b.id)
and a.sub_id in (select c.sub_id
from tab1 c
group by c.sub_id,c.evt_id
having count(1) = 1)
For multiple database vendors I think the easiest solution would be a couple of not exists queries:
select *
from tab1 a
where not exists (
select 1
from tab2 b
where a.id = b.id
)
and not exists (
select 1
from tab1 c
where c.sub_id = a.sub_id
and c.evt_id = a.evt_id
and c.id <> a.id
)
i think below query will work
select a.*
from tab1 a
where not exists (select 1 from tab2 b where a.id = b.id)
and not exists (select 1 from tab1 c
where c.sub_id = a.sub_id
and c.childid = a.childid
group by c.sub_id,childid
having count(*)> = 2
)
Just to add an approach using a CTE, you can first determine the unique childid,subid pairs and then join that table with your main table:
DB Fiddle
Schema (PostgreSQL v9.6)
create table tab1 (
id integer primary key unique not null
, subid integer not null
, childid integer not null
);
insert into tab1 (id,subid,childid) values (1, 11, 77);
insert into tab1 (id,subid,childid) values (2, 22, 55);
insert into tab1 (id,subid,childid) values (3, 33, 66);
insert into tab1 (id,subid,childid) values (4, 11, 77);
insert into tab1 (id,subid,childid) values (7, 22, 55);
insert into tab1 (id,subid,childid) values (8, 33, 60);
insert into tab1 (id,subid,childid) values (9, 99, 98);
insert into tab1 (id,subid,childid) values (10, 33,60);
insert into tab1 (id,subid,childid) values (11, 97 ,98);
create table tab2 (
id integer primary key unique not null
);
insert into tab2 (id) values (1);
insert into tab2 (id) values (4);
insert into tab2 (id) values (7);
insert into tab2 (id) values (10);
Query #1
with tab1_unique as (
select subid, childid, count(*) as duplicate
from tab1
group by subid, childid
having count(*) = 1
)
select *
from tab1 a
join tab1_unique u on a.subid = u.subid and a.childid = u.childid
where not exists (select 1 from tab2 b where a.id = b.id);
| id | subid | childid | subid | childid | duplicate |
| --- | ----- | ------- | ----- | ------- | --------- |
| 11 | 97 | 98 | 97 | 98 | 1 |
| 9 | 99 | 98 | 99 | 98 | 1 |
| 3 | 33 | 66 | 33 | 66 | 1 |
not exists should do this:
select t1.*
from (select t1.*, count(*) over (partition by subid, childid) as cnt
from tab1 t1
) t1
where not exists (select 1 from tab2 t2 where t2.id = t1.id) and
cnt = 1;
You can use not exists as well for the subid/childid with the assumption that the rows are unique in the first table. Without this assumption, window functions are best solution -- and possibly the best solution anyway.

How to improve simple but inefficient parent/child relationship query

I have a simple table as described below, where there's a possible parent/child relationship. This is actually a very large table, but this is an fair representation. There are no "grandchildren" relationships.
I need to transform this into a slightly different table which is filtered on some input value.
declare #pc table ( myId char(1) not null, parentId char(1) );
insert into #pc (myId, parentId) values ('A', null)
insert into #pc (myId, parentId) values ('B', 'A')
insert into #pc (myId, parentId) values ('C', 'A')
insert into #pc (myId, parentId) values ('D', null)
insert into #pc (myId, parentId) values ('E', null)
insert into #pc (myId, parentId) values ('F', 'E')
insert into #pc (myId, parentId) values ('G', null)
insert into #pc (myId, parentId) values ('H', 'G')
insert into #pc (myId, parentId) values ('I', 'G')
insert into #pc (myId, parentId) values ('J', 'G')
insert into #pc (myId, parentId) values ('K', null)
-- This is the results I need
declare #target table ( myId char(1) not null, parentId char(1), hasFamily bit );
Given input of "A", then I need three rows like this:
A NULL 1
B A 1
C A 1
In other words, I need everything that belongs to the "family group" A.
Given "B" I need the same output, everything in B's family group (which happens to be A's):
A NULL 1
B A 1
C A 1
Given "D" I need just the one line since no-one is in D's family:
D NULL 0
Given null, I need the entire table data set but with the proper rows marked as "having family" or not.
Here's my attempt which is technically correct, but it's not efficient at all taking 3 passes at the data to do it:
declare #testcase char(1) = 'B';
-- The inefficient method
INSERT INTO #target( myId,parentId )
SELECT pc.myId, pc.parentId
FROM #pc pc
WHERE(pc.myId = ISNULL(#testcase, pc.myId))
OR (pc.parentId =#testcase);
INSERT INTO #target( myId,parentId )
SELECT pc.myId, pc.parentId
FROM #pc pc
WHERE pc.myId IN ( SELECT parentId FROM #target )
AND pc.myId NOT IN ( SELECT myId FROM #target );
update t
set t.hasFamily = 1
from #target t
left outer join #target t2
on t.myId = t2.parentId
where t.parentId is not null or t2.myId is not null;
Can you see a better way of looking at this problem?
A window function to count children, made available on every row, simplifies the wanted queries. How you chose to deploy it is u to you. You might choose to use persisted computed columns for example, or triggers. If scale and performance are issues then you would need to consider the indexing and inspect execution plans.
Perhaps I don't see the complexity from the small model you have presented here hence my suggestions may be too simplistc.
Demo at SQL Fiddle
create table Table1 ( myId char(1) not null, parentId char(1) );
insert into Table1 (myId, parentId) values ('A', null);
insert into Table1 (myId, parentId) values ('B', 'A');
insert into Table1 (myId, parentId) values ('C', 'A');
insert into Table1 (myId, parentId) values ('D', null);
insert into Table1 (myId, parentId) values ('E', null);
insert into Table1 (myId, parentId) values ('F', 'E');
insert into Table1 (myId, parentId) values ('G', null);
insert into Table1 (myId, parentId) values ('H', 'G');
insert into Table1 (myId, parentId) values ('I', 'G');
insert into Table1 (myId, parentId) values ('J', 'G');
insert into Table1 (myId, parentId) values ('K', null);
create view too_simplistic as
select
myId
, parentId
, coalesce(parentId, myId) parent_flat
, case when count(*) over(partition by coalesce(parentId, myId))-1 = 0
then 0
else 1
end as hasFamily
from table1
;
Query 1:
declare #want char(1) = 'A'
select myId, parentId, hasFamily
from too_simplistic
where parent_flat = (select parent_flat from too_simplistic where MyId = #want)
or MyId = #want
Results:
| myId | parentId | hasFamily |
|------|----------|-----------|
| A | (null) | 1 |
| B | A | 1 |
| C | A | 1 |
Query 2:
declare #want char(1) = 'B'
select myId, parentId, hasFamily
from too_simplistic
where parent_flat = (select parent_flat from too_simplistic where MyId = #want)
or MyId = #want
Results:
| myId | parentId | hasFamily |
|------|----------|-----------|
| A | (null) | 1 |
| B | A | 1 |
| C | A | 1 |
Query 3:
declare #want char(1) = 'D'
select myId, parentId, hasFamily
from too_simplistic
where parent_flat = (select parent_flat from too_simplistic where MyId = #want)
or MyId = #want
Results:
| myId | parentId | hasFamily |
|------|----------|-----------|
| D | (null) | 0 |
Query 4:
select *
from too_simplistic
Results:
| myId | parentId | parent_flat | hasFamily |
|------|----------|-------------|-----------|
| A | (null) | A | 1 |
| B | A | A | 1 |
| C | A | A | 1 |
| D | (null) | D | 0 |
| E | (null) | E | 1 |
| F | E | E | 1 |
| G | (null) | G | 1 |
| H | G | G | 1 |
| I | G | G | 1 |
| J | G | G | 1 |
| K | (null) | K | 0 |
select d.*
, case when children = 0 then 0 else 1 end as hasFamily
from (
select *
, count(*) over(partition by coalesce(parentId, myId))-1 children
from #pc
) d
;

SQL for the below requirements

I am having trouble framing a SQL to get the desired outputs.
Table X
Id_X | GroupId | SomeColumn
Table R
Id_R | Id_X | GroupId | RColumn
The objective is to pick Id_X from Table R that have only GroupId values (A,B) and RColumn value is RValue
Ex:
Table X
1 | A | SomeValue
1 | B | SomeValue
2 | A | SomeValue
2 | B | SomeValue
2 | B | SomeValue
2 | C | SomeValue
Table R
101 | 1 | A | RValue
102 | 2 | A | RValue
The SQL should return 1
SELECT
[X].Id_X
FROM
[R]
INNER JOIN [X] ON
[R].Id_X = [X].Id_X
AND
[R].GroupId = [X].GroupId
WHERE
[X].GroupId IN ( 'A', 'B' )
AND
[R].RColumn = 'RValue'
If i understand correctly, your query should be
DECLARE #TableX AS TABLE
(
Id_X int, GroupId varchar(10), SomeColumn varchar(20)
)
INSERT INTO #TableX
VALUES
( 1, 'A', 'SomeValue'),
( 1, 'B', 'SomeValue'),
( 2, 'A', 'SomeValue'),
( 2, 'B', 'SomeValue'),
( 2, 'B', 'SomeValue'),
( 2, 'C', 'SomeValue')
DECLARE #TableR AS TABLE
(
ID_R int, Id_X int, GroupId varchar(10),RColumn varchar(10)
)
INSERT INTO #TableR
VALUES (101,1,'A','RValue'), (102,2,'A','RValue')
SELECT DISTINCT tr.Id_X
FROM #TableR tr
INNER JOIN #TableX tx ON tx.Id_X = tr.Id_X AND tx.GroupId = tr.GroupId
WHERE tr.RColumn = 'RValue'
AND NOT EXISTS ( SELECT 1 FROM #TableX tx2
WHERE tx2.Id_X = tx.Id_X
AND tx2.GroupId NOT IN ('A','B')
)
Demo link: http://rextester.com/EGLOT75874