I have a database table that has the following structure:
TABLE_A
DOC_ID | STATUS
1 | 0
2 | 1
TABLE_B
PK_ID | DOC_ID | NAME | VALUE
1 | 1 | A | 1
2 | 1 | B | 2
3 | 2 | A | 1
4 | 2 | B | 1
5 | 2 | C | 1
DOC_ID is the FOREIGN KEY on TABLE_B.
Then I create a VIEW so that I may more easily sort on NAME.
CREATE VIEW [dbo].[V_MY_VIEW] AS
SELECT a.DOC_ID, a1.VALUE AS 'A', a2.VALUE AS 'B', a3.VALUES AS 'C'
FROM dbo.TABLE_A a,
( SELECT DOC_ID, VALUE FROM dbo.TABLE_B WHERE NAME = 'A') a1
LEFT OUTER JOIN ( SELECT DOC_ID, VALUE FROM dbo.TABLE_B WHERE NAME = 'B') a2
ON a1.DOC_ID = a2.DOC_ID
LEFT OUTER JOIN ( SELECT DOC_ID, VALUE FROM dbo.TABLE_B WHERE NAME = 'C') a3
ON a1.DOC_ID = a3.DOC_ID
WHERE a.STATUS IN (0, 1)
This view will only include the rows with DOC_ID = 2 since the rows with DOC_ID = 1 do not have a row with NAME = C. How should I modify the VIEW so that it will include all the rows from TABLE_B?
Thanks.
CREATE VIEW [dbo].[V_MY_VIEW] AS
SELECT a.DOC_ID, a1.VALUE AS A, a2.VALUE AS B, a3.VALUE AS C
FROM dbo.TABLE_A a
LEFT JOIN (SELECT DOC_ID, VALUE FROM dbo.TABLE_B WHERE NAME = 'A') a1
ON a.DOC_ID = a1.DOC_ID
LEFT OUTER JOIN ( SELECT DOC_ID, VALUE FROM dbo.TABLE_B WHERE NAME = 'B') a2
ON a.DOC_ID = a2.DOC_ID
LEFT OUTER JOIN ( SELECT DOC_ID, VALUE FROM dbo.TABLE_B WHERE NAME = 'C') a3
ON a.DOC_ID = a3.DOC_ID
WHERE a.STATUS IN (0, 1)
Look the results at http://sqlfiddle.com/#!3/5574b/4/0
SELECT * FROM TABLE_A LEFT OUTER JOIN TABLE_B ON TABLE_A.DOC_ID = TABLE_B.DOC_ID
WHERE TABLE_A.STATUS IN (0, 1)
Replace * with the columns you want to display.
Include an ISNULL(Name, ) in your order by clause unless you want none matches at the top.
Related
The request below:
SELECT foos.id,bars.name
FROM foos
JOIN bar_foo ON (bar_foo.foo_id = id )
JOIN bars ON (bars.id = bar_foo.bar_id )
returns a list like this:
id | name
---+-----
1 | a
1 | b
2 | a
2 | y
2 | z
3 | a
3 | b
3 | c
3 | d
How to get the ids for which id must have at least a and b, and more generally the content of a given array ?
From the example above, I would get:
id | name
---+-----
1 | a
1 | b
3 | a
3 | b
3 | c
3 | d
For two values, you can use windowing boolean aggregation:
select *
from (
select f.id, b.name,
bool_or(b.name = 'a') over(partition by id) has_a,
bool_or(b.name = 'b') over(partition by id) has_b
from foos f
join bar_foo bf on bf.foo_id = f.id
join bars b on b.id = bf.bar_id
) t
where has_a and has_b
A more generic approach uses array aggregation:
select *
from (
select f.id, b.name,
array_agg(b.name) over(partition by id) arr_names
from foos f
join bar_foo bf on bf.foo_id = f.id
join bars b on b.id = bf.bar_id
) t
where arr_names #> array['a', 'b']
So i have:
Table A with (not only Name - Multiple Columns):
| IDA | Name |
|:-----------|------------:|
| 1 | John |
| 2 | Jonas |
| 3 | Carl |
| 4 | Foo |
| 5 | Bar |
| 6 | Dog |
Table B with (not only Name - Multiple Columns):
| IDB | Name |
|:-----------|------------:|
| 1 | Bob |
| 2 | Lisa |
Table MAIN with:
| ID | FKtoB | FKtoA |
|:-----------|------------:|------------:|
| 1 | 1 | 4 |
| 2 | | 3 |
| 3 | | 1 |
| 4 | 2 | 6 |
| 5 | | 2 |
| 6 | | 5 |
My goal is to select from MAIN and prefer to use the data of Table B but if not Available i will use the data of Table A. I tryed to use coalesce but that didn't help because i dont want to mix the data, so if for example Table B has the data for one row, then in this row there should be no data from Table A.
My Query looks like that:
select
coalesce(b.name, a.name) as surname, coalesce(b.surname, a.surname) as surname
from MAIN m
left join TableA a on a.IDA = m.FKtoA
left join TableB b on b.IDA = m.FKtoB
My Problem there is that if for example b.name is filled and b.surname isn't it would mix b.name with a.surname, thats not okay in my case.
Maybe you could add the non-existens of b as join criteria:
select
coalesce(b.name, a.name) as name, coalesce(b.surname, a.surname) as surname
from MAIN m
left join TableB b on b.IDA = m.FKtoB
left join TableA a on a.IDA = m.FKtoA and b.IDA is null
EDIT: corrected one surname to name
Try Left Join with Case -
SELECT
M.ID, (CASE WHEN B.IDB IS NOT NULL THEN B.Name ELSE A.Name END) AS NAME
FROM
MAIN AS M
LEFT JOIN TableB as B ON B.IDB = M.FKtoB
LEFT JOIN TableA as A ON A.IDA = M.FKtoA
From SQL Server 2012 (and higher) you can use IIF:
SELECT
IIF(m.FKtoB IS NULL, A.name, B.name) AS surname
FROM MAIN m
LEFT JOIN TableA a ON a.IDA = m.FKtoA
LEFT JOIN TableB b ON b.IDA = m.FKtoB
try this:
declare #tableA table(IDA INT,Name varchar(MAX))
declare #tableB table(IDB INT, Name VARCHAR(MAX))
declare #tableMain table(ID INT,FKtoB INT,FKtoA INT)
INSERT INTO #tableA
SELECT 1,'John'
union all
SELECT 2, 'Jonas'
union all
SELECT 3, 'Carl'
union all
SELECT 4,'Foo'
union all
SELECT 5 ,'Bar'
union all
SELECT 6,'Dog'
INSERT INTO #tableB
SELECT 1, 'Bob'
Union all
SELECT 2, 'Lisa'
INSERT INTO #tableMain
SELECT 1,1,4
union all
SELECT 2,null,3
union all
SELECT 3,null,1
union all
SELECT 4,2,6
union all
SELECT 5,null,2
union all
SELECT 6,null,5
Select tm.Id,ISNULL(tb.Name,ta.Name) As NAME from #tableMain tm
LEFT JOIN #tableB tb on tm.FktoB=tb.IDb and IDb is not NUll
LEFT JOIN #tableA tA on tm.FktoA=ta.IDA and FktoB is NUll
You want to select from table A only when there is no match in table B, so given foreign keys are intact:
select
coalesce(b.firstname, a.firstname) as firstname,
coalesce(b.surname, a.surname) as surname,
coalesce(b.birthday, a.birthday) as birthday,
...
from MAIN m
left join TableB b on b.IDB = m.FKtoB
left join TableA a on a.IDA = m.FKtoA and m.FKtoB is null;
Another approach would be union all:
select firstname, surname, birthday, ...
from TableB
where IDB in (select FKtoB from MAIN)
union all
select firstname, surname, birthday, ...
from TableA
where IDA in (select FKtoA from MAIN where FKtoB is null);
Try this. It will give result as you want.
(select ida as ID ,
case when ida is not null then ida end fkToIda,
case when idb is not null then idb end fkToIdB
from A
left outer join B on a.ida=b.idb)
union
(select idb ,
case when ida is not null then ida end,
case when idb is not null then idb end
from A
right outer join B on a.ida=b.idb)
i have some problems with join tables:
Table A -> ID,Col1,Col2,Col3
Table B -> Rank , ColX , A_ID (Relationship with A.ID)
I want to take higher Rank (each A_ID , like group by A_ID) of B table
my results must be something like A.ID , Col1 , Col2 , Col3 , ""ColX"" , how can i do that ?
and i want my result count equals to A.ID count.
TableA
+--------------------+
| ID|Col1|Col2|Col3| |
+--------------------+
| 1 | C1 | C2 | C3 |
| 2 | C1 | C2 | C3 |
+--------------------+
TABLE_B
+-----------------------------+
| ID| COL_X |RANK |A_ID| |
+-----------------------------+
| 1 | SomeValue | 1 | 1 |
| 2 | some22222 | 2 | 1 |
| 3 | SOMEXXXX | 3 | 1 |
| 4 | SOMEVAL | 1 | 2 |
| 5 | VALUE | 2 | 2 |
+-----------------------------+
Expected Output:
+--------------------------------------------------------------------+
| ID| Col1| Col2 | Col3| COLX |
+--------------------------------------------------------------------+
| 1 | C1 | C2 | C3 | SOMEXXXX (Higher Rank of TableB-> A_ID = 1) |
| 2 | C1 | C2 | C3 | VALUE (Higher Rank of TableB-> A_ID = 2) |
+--------------------------------------------------------------------+
You could easily do this using a subquery by first finding the max for each A_ID and then joining to tableA and TableB to get your desired rows:
SELECT a.ID,
a.col1,
a.Col2,
a.Col3,
b1.Col_X
FROM (
SELECT a_id
,max(rank) AS MaxRank
FROM tableb
GROUP BY a_id
) b
INNER JOIN tablea a ON a.id = b.a_id
INNER JOIN tableb b1 ON b.a_id = b1.a_id AND b1.rank = b.MaxRank
ORDER BY a.ID;
SQL Fiddle Demo
I'm thinking you want to take the max rank from your table b for each row in table a?
There's lots of different ways of approaching this. Here's one simple one:
with maxCTE as
(select
a_id,
max(rank) as MaxRank
from
tableb
group by
a_id
)
select
*
from
tablea a
inner join tableb b
on a.id = b.a_id
inner join maxcte c
on b.a_id = c.a_id
and b.rank = c.MaxRank
SQLFiddle
Basically, the CTE identifies the max rank for each a_id, then we join that back to tableb to get the details about that row.
with x as
(select a_id, max(rank) as mxrnk
from tableB
group by a_id)
select a.id, a.col1, a.col2, a.col3, b.col_x
from tableA a join x
on a.id = x.a_id
join tableB b
on x.mxrnk = b.rank
You can select max rank per a_id in the cte and use it to select the corresponding columns.
One is to INNER JOIN Table B onto Table A by ID's. You will have 3 records returned from Table B. If you ORDER those records by the COLX
SELECT
,a.ID
,a.Col1
,a.Col2
,a.Col3
,b.COLX
FROM TableA AS a
INNER JOIN TABLE_B AS b on b.A_ID = a.id
ORDER BY b.COLX DESC
Then another way is joining a sub query of Table B that also has a sub query that filters Table B records to only the records with the highest RANK.
That way you can bring in COLX values from the highest RANK records from Table B that match the records of Table A.
I think at least...
SELECT
a.ID
,a.Col1
,a.Col2
,a.Col3
,b.COLX
FROM TableA a
INNER JOIN (
SELECT
a.A_ID
,a.RANK
,a.COLX
FROM TABLE_B a
INNER JOIN (
SELECT
A_ID,
,MAX(RANK) AS [RANK] -- Highest Rank
FROM TABLE_B
GROUP BY A_ID
) AS b ON b.A_ID = a.A_ID AND b.RANK = a.RANK
) AS b on b.A_ID = a.id
ORDER BY a.ID ASC
Select A.*,D.Col_X
from
(Select C.COL_X,B.A_ID
from
(Select A_ID,MAX(rank) as MAX_rank
from TABLE_B
group by A_ID) B ----- gets the highest rank and ID of the highest rank
inner join TABLE_B c
on
concat(C.A_ID,C.RANK)= concat(B.A_ID,B.MAX_rank)) D ---- Gets the highest rank name
inner join TABLE_A A
on D.A_ID=A.ID
OUTPUT:
ID Col1 Col2 Col3 Col_X
1 c1 c2 c3 SOMEXXXX
2 c1 c2 c3 VALUE
ID | NAME | REFERENCE | STATUS
-------------------------------
1 | name1 | 123 | 0
2 | name1 | 123 | 1
3 | name2 | 111 | 0
4 | name3 | 222 | 0
5 | name5 | 555 | 1
Let's say I have this table TABLE1. I need a select statement that will only find records with STATUS=0, but if "same" record exists with STATUS=1 (like those records with ID 1 and 2)
So, query must find only third and fourth record.
Based on your suggested results, I am reading your question as "find only records with status = 0 where the same name/reference combination doesn't have a record with status = 1".
One way you can do this is with a not exists clause:
select t.*
from table t
where status = 0 and
not exists (select 1
from table t2
where t2.name = t.name and
t2.reference = t.reference and
t2.status = 1
);
Try this query out:
SELECT A.*
FROM TABLE1 A
LEFT JOIN TABLE1 B ON
A.ID = B.ID AND
A.NAME = B.NAME AND
A.REFERENCE = B.REFERENCE AND
B.STATUS = 1
WHERE A.STATUS = 0
AND B.REFERENCE IS NULL
The NULL check on B.REFERENCE in the WHERE clause basically ensures that there was no matching record found in the LEFT JOIN (you could just use B.ID or B.NAME also).
MINUS can be used to get the value with Status both 0 but not 1.
SELECT ID, Name, Reference, Status
FROM Table1
WHERE (Name, Reference) IN (SELECT Name, Reference
FROM Table1
WHERE Status = 0
MINUS
SELECT Name, Reference
FROM Table1
WHERE Status = 1)
SELECT * FROM TABLE WHERE NAME NOT IN
(
SELECT NAME FROM
TABLE
GROUP BY NAME
HAVING COUNT(DISTINCT STATUS)>1
) AND STATUS='0';
I have TableA (id bigint, name varchar) and TableB (name varchar) that contains the following data:
Table A: Table B: Results:
------------- --------- ---------------
| 1 | "A" | | "A" | | 1 | "A" |
| 1 | "B" | | "B" | | 1 | "B" |
| 2 | "A" | --------- | 4 | "A" |
| 3 | "B" | | 4 | "B" |
| 4 | "A" | ---------------
| 4 | "B" |
-------------
I want to return results from TableA that contains an EXACT match of what's in table B.
Using the 'IN' clause only retrieves back an occurrence.
Also, another example, if TableB has only "A", I want it to return back: 2-"A"
I understand your question but it is a tricky one as not exactly in line with the relational logic. You are looking for id's for which SELECT name FROM TableA WHERE id IN ... ORDER BY name; is identical to SELECT name FROM B order by name;.
Can you assume that A(id,name) is unique and B(name) is unique? Better said, are there constraints like that or can you set them up?
If yes, here is a solution:
1. Get rid of ids in A with rows not matching the rows in B
SELECT id, A.name FROM A WHERE id NOT IN
(SELECT id FROM A LEFT JOIN B ON A.name = B.name WHERE B.name IS NULL);
2. Count rows per each id (this is why the unique constraints are necessary)
SELECT id, COUNT(*) FROM
(
SELECT id, A.name FROM A WHERE id NOT IN
(SELECT id FROM A LEFT JOIN B ON A.name = B.name WHERE B.name IS NULL)
) t
GROUP BY id;
3. Only retain those that match the number of rows of B.
SELECT id, COUNT(*) FROM
(
SELECT id, A.name FROM A WHERE id NOT IN
(SELECT id FROM A LEFT JOIN B ON A.name = B.name WHERE B.name IS NULL)
) t
GROUP BY id
HAVING COUNT(*) = SELECT COUNT(*) FROM B;
This works in SQL Server
select * from TableA a
where
(select count(*) from TableB) = (select count(*) from TableA where id = a.id) and
(select count(*) from TableB) =
(
select count(*) from
(
select name from TableA where id = a.id
intersect
select name from TableB
) as b
)