Join two tables, select column based on value in first table - vb.net

Imagine a table A with two columns "Type" and "Severity", and a table B with columns "Type", "Severity_1", "Severity_2", "Severity_3", "Severity_4".
A.Severity is an integer, and all the B.Severity_* fields contain a description.
I want to query table A for Type and Severity, and also return a third column with the corresponding description from table B.
Currently, I'm using LINQ and have a set of nested IF statements in the select clause. Is there a way to project table B or select out each {Type, Severity, Severity_*} record and union the results?

You should make a view, if possible, like this
select a.Type, a.Severity,
case a.Severity
when 1 then b1.Severity_1
when 2 then b2.Severity_2
when 3 then b3.Severity_3
when 4 then b4.Severity_4
end as Description
from TableA a
left join tableb b1 on a.Severity = 1 and a.Type = b1.Type
left join tableb b2 on a.Severity = 2 and a.Type = b2.Type
left join tableb b3 on a.Severity = 3 and a.Type = b3.Type
left join tableb b4 on a.Severity = 4 and a.Type = b4.Type
Then just query the view in Linq.

Related

Return all inactive items from Table B for each item in Table A

Not sure if I can explain this well in words..
I want to find all items in Table B that have ALL items inactive for each item in Table A. Let's say Table A just has column "item" and Table B has columns "item" and "active"
Table A Table B
A A | 1
A A | 1
A B | 1
B B | 0
B C | 0
B C | 0
C D | 0
C E | 1
D
E
F
In that example, it should return C and D.
I managed to join tables A and B together with a group by clause but not sure where to go from there. Tried looking around and only found answers where the other table's value doesn't exist so you can use "NOT IN" but that doesn't seem to work here.
Any help would be appreciated!
You can join the tables and use the HAVING clause to make the comparison like this:
SELECT ta.Item
FROM TableA tA
LEFT JOIN TableB tB
ON tA.Item=tB.Item
GROUP BY tA.Item
HAVING SUM(tB.Inactive)=COUNT(tb.Inactive)
This would give you a distinct list of Items in TableA
The above query assumes 1 is Inactive and 0 is Active. If your data is opposite (which it looks like it is), you could instead say:
SELECT ta.Item
FROM TableA tA
LEFT JOIN TableB tB
ON tA.Item=tB.Item
GROUP BY tA.Item
HAVING SUM(tB.Inactive)=0
This would also return Item F as it doesn't have a value in table b to SUM. Just flip the LEFT JOIN to an INNER JOIN if you don't want Item F to return.
If you need to return back all instances in TableA, you could use a subquery and join to that:
SELECT ta.Item
FROM TableA tA
LEFT JOIN (SELECT ITEM, SUM(ACTIVE) Sum0 FROM TableB GROUP BY ITEM)tB
ON tA.Item=tB.Item
WHERE tB.Sum0 = 0
/*or tB.Sum0 is null would give you Item F*/
Use NOT EXISTS:
select distinct a.item
from table_A a
where not exists (select 1 from table_B b where b.item = a.item and b.status = 1);
I believe you would just need to join the tables together on table name and filter to only value of 0 on the active column.
SELECT B.*
FROM TableA A INNER JOIN TableB B ON A.item = B.item
WHERE B.active = 0
Does that get you what you need?

Select full outer join from many-to-many relationships

I am trying to do something in MSSQL which I suppose is a fairly simple and common thing in any database with many-to-many relationships. However I seem to always end up with a quite complicated select query, I seem to be repeating the same conditions several times to get the desired output.
The scenario is like this. I have 2 tables (table A and B) and a cross table with foreign keys to the ID columns of A and B. There can only be one unique pair of As and Bs in the crosstable (I guess the 2 foreign keys make up a primary key in the cross table ?!?). Data in the three tables could look like this:
TABLE A TABLE B TABLE AB
ID Type ID Type AID BID
--------------------------------------------------
R Up 1 IN R 3
S DOWN 2 IN T 3
T UP 3 OUT T 5
X UP 4 OUT Z 6
Y DOWN 5 IN
Z UP 6 OUT
Now let's say I select all rows in A of type UP and all rows in B of type OUT:
SELECT ID FROM A AS A1
WHERE Type = 'UP'
(Result: R, T, X, Z)
SELECT ID FROM B AS B1
WHERE Type = 'OUT'
(Result: 3, 4, 6)
What I want now is to fully outer join these 2 sub queries based on the relations listed in AB. Hence I want all IDs in A1 and B1 to be listed at least once:
A.ID B.ID
R 3
T 3
null 4
X null
Z 6
From this results set I want to be able to see:
- Which rows in A1 does not relate to any rows in B1
- Which rows in B1 does not relate to any rows in A1
- Relations between rows in A1 and B1
I have tried a couple of things such as:
SELECT A1.ID, B1.ID
FROM (
SELECT * FROM A
WHERE Type = 'UP') AS A1
FULL OUTER JOIN AB ON
A1.ID = AB.AID
FULL OUTER JOIN (
SELECT * FROM B
WHERE Type = 'OUT') AS B1
ON AB.BID = B1.ID
This doesn't work, since some of the relations listed in AB are between rows in A1 and rows NOT IN B1 OR between rows in B1 but NOT IN A1.
In other words - I seem to be forced to create a subquery for the AB table also:
SELECT A1.ID, B1.ID
FROM (
SELECT * FROM A
WHERE Type = 'UP') AS A1
FULL OUTER JOIN (
SELECT * FROM AB AS AB1
WHERE
AID IN (SELECT ID FROM A WHERE type = 'UP') AND
BID IN (SELECT ID FROM B WHERE type = 'OUT')
) AS AB1 ON
A1.ID = AB1.AID
FULL OUTER JOIN (
SELECT * FROM B
WHERE Type = 'OUT') AS B1
ON AB1.BID = B1.ID
That just seems like a rather complicated solution for a seemingly simply problem. Especially when you consider that for A1 and B1 subqueries with more (complex) conditions - possible involving joins to other tables (one-to-many) would require the same temporary joins and conditions to be repeated in the AB1 subquery.
I am thinking that there must be an obvious way to rewrite the above select statements in order to avoid having to repeat the same conditions several times. The solution is probably right there in front me, but I just can't see it.
Any help would be appreciated.
I think you could employ a CTE in this case, like this:
;WITH cte AS (
SELECT A.ID AS AID, A.Type AS AType, B.ID AS BID, B.Type AS BType
FROM A FULL OUTER JOIN AB ON A.ID = AB.AID
FULL OUTER JOIN B ON B.ID = AB.BID)
SELECT AID, BID FROM CTE WHERE AType = 'UP' OR BType = 'OUT'
The advantage of using a CTE is that it will be compiled once. Then you can add additional criteria to the WHERE clause outside the CTE
Check this SQL Fiddle

SQL where clause for left outer join

I have a problem with a view I want to create. I have two tables joined in a left outer join, say tableA and tableB, where tableB is left outer joined.
I want to select only those rows from table B where state equals 4, so I add WHERE state = 4 to my query. Now the result set is trimmed quite a bit because all rows without a matching row in tableB are removed from the result (since state isn't 4 for those rows). I also tried WHERE state = 4 OR state IS NULL, doesn't work either (since state technically isn't NULL when there is no state).
So what I need is a WHERE statement which is only evaluated when there actually is a row, does such a thing exist?
If not I see two options: join (SELECT * FROM tableB WHERE state = 4) instead of table B, or create a view with the same WHERE statement and join that instead. What's the best option performance wise?
This is SQL Server 2008 R2 by the way.
You put the conditions in the on clause. Example:
select a.this, b.that
from TableA a
left join TableB b on b.id = a.id and b.State = 4
You can add state = 4 to the join condition.
select *
from T1
left outer join T2
on T1.T1ID = T2.T1ID and
T2.state = 4
Even easier than a subquery is expanding the on clause, like;
select *
from TableA a
left join
TableB b
on a.b_id = b.id
and b.state = 4
All rows from TableA will appear, and only those from TableB with state 4.
SQL Server will probably execute the view, expanded on, and subquery in exactly the same way. So performance wise, there should be little difference.
Alternative approach: (1) inner join to table B where state equals 4, (2) antijoin to table B to find rows that don't exist, (3) union the results:
SELECT A1.ID, A1.colA, B1.ColB
FROM tableA AS A1
INNER JOIN tableB AS B1
ON A1.ID = B1.ID
AND B1.state = 4
UNION
SELECT A1.ID, A1.colA, '{{MISSING}}' AS ColB
FROM tableA AS A1
WHERE NOT EXISTS (
SELECT *
FROM tableB AS B1
WHERE A1.ID = B1.ID
);
Alternatively:
SELECT A1.ID, A1.colA, B1.ColB
FROM tableA AS A1
JOIN tableB AS B1
ON A1.ID = B1.ID
AND B1.state = 4
UNION
SELECT ID, colA, '{{NA}}' AS ColB
FROM tableA
WHERE ID IN (
SELECT ID
FROM tableA
EXCEPT
SELECT ID
FROM tableB
);

select row from table and substitute a field with one from another column if it exists

I'm trying construct a PostgreSQL query that does the following but so far my efforts have been in vain.
Problem:
There are two tables: A and B. I'd like to select all columns from table A (having columns: id, name, description) and substitute the "A.name" column with the value of the column "B.title" from table B (having columns: id, table_A_id title, langcode) where B.table_A_id is 5 and B.langcode is "nl" (if there are any rows).
My attempts:
SELECT A.name,
case when exists(select title from B where table_A_id = 5 and langcode= 'nl')
then B.title
else A.name
END
FROM A, B
WHERE A.id = 5 and B.table_A_id = 5 and B.langcode = 'nl'
-- second try:
SELECT COALESCE(B.title, A.name) as name
from A, B
where A.id = 5 and B.table_A_id = 5 and exists(select title from B where table_A_id = 5 and langcode= 'nl')
I've tried using a CASE and COALESCE() but failed due to my inexperience with both concepts.
Thanks in advance.
araqnid's is the answer you are looking for, I bet.
But if you want to enforce that no more than one row is returned for each original matching A row, you might prefer to do a subselect instead of a LEFT JOIN. For example:
SELECT A.id, COALESCE(
( SELECT max(B.title) FROM B WHERE
langcode = 'nl' AND B.table_a_id = A.id), A.name ) as name
FROM A
WHERE A.id = 5
I use "max" here to select an arbitrary value, in the event there is more than one. You can use "min" or whatever you consider appropiate in your case.
Perhaps this is more easy to understand than the LEFT JOIN, but (apart from the two being not exactly equivalent) a JOIN will perform better than N subselects (much better is N is large).
Anyway, from a learning point of view, it's good to understand both.
select A.id, coalesce(B.title, A.name)
from TableA AS A
left join (select table_a_id, title from TableB where langcode = 'nl') AS B
on B.table_a_id = A.id
WHERE A.id = 5
Ok, I'm not sure how your tables have to be joined, but something like this should do the job:
SELECT yourcolumnlist,
CASE WHEN A.name IS NULL THEN B.title ELSE A.name END
FROM TableA AS A
INNER JOIN TableB AS B
ON A.id = B.table_A_id
WHERE B.table_A_id = 5
AND B.langcode = 'nl'
Another way to do it would be to use the COALESCE() function:
SELECT yourcolumnlist,
COALESCE(A.name, B.title)
FROM TableA AS A
INNER JOIN TableB AS B
ON A.id = B.table_A_id
WHERE B.table_A_id = 5
AND B.langcode = 'nl'
Try this
select A.id,B.title,A.description from TableA as A inner join tableB as B
on A.id=B.id where B.table_A_id = 5 and B.langcode ='nl'

Return single row when 2 records in right table

I have two related sql server tables ... TableA and TableB.
***TableA - Columns***
TableA_ID INT
VALUE VARCHAR(100)
***TableB - Columns***
TableB_ID INT
TableA_ID INT
VALUE VARCHAR(100)
For every single record in TableA there are always 2 records in TableB. Therefore TableA has a one-to-many relationship with TableB.
How could I write a single sql statement to join these tables and return a single row for each row in TableA that includes:
a column for the VALUE column in the first related row in table B
a column for the VALUE column in the second related row in table B?
For exactly 2 related records, that's easy. Join table B twice:
SELECT
A.TableA_ID,
A.VALUE AS VALUE_A
B1.VALUE AS VALUE_B1
B2.VALUE AS VALUE_B2
FROM
TableA AS A
INNER JOIN TableB B1 ON B1.TableA_ID = A.TableA_ID
INNER JOIN TableB B2 ON B2.TableA_ID = A.TableA_ID
WHERE
B1.TableB_ID < B2.TableB_ID
If you have a column in table B that determines what is "first value" and what is "second value", this gets even easier (and it would work like this for N columns in table B, just add more joins):
SELECT
A.TableA_ID,
A.VALUE AS VALUE_A
B1.VALUE AS VALUE_B1
B2.VALUE AS VALUE_B2
FROM
TableA AS A
INNER JOIN TableB B1 ON B1.TableA_ID = A.TableA_ID AND B1.Type = '1'
INNER JOIN TableB B2 ON B2.TableA_ID = A.TableA_ID AND B2.Type = '2'
A composite index on TableB over (TableA_ID, Type) helps this join.