TSQL select max from 3 columns and then inner join a column - sql

How do I do this:
in table A I have 3 columns with integer values: a,b,c
in table B I have 3 columns with integer values: x,y,z
I want to select max of (a,b,c) and inner join the corresponding value from x,y,z.
Example:
a = 1, b = 2, c = 3
x = 7, y = 8, z = 9
result:
table with two columns
firstColumn | secondColumn
c | 9
explanation: c is the max of a,b,c and we take the name, not the value
9 is the value from the second table what we need
thanks in advance
ps I am using sql server 2014
Edit: I tried to make an example with excel table

Unless both tables have only 1 row you'll propably don't want to do a CROSS JOIN that would join all records from table1 to all from table2.
So my guess is that you're looking for something like this:
select
case
when c > isnull(a,0) and c > isnull(b,0) then 'c'
when b > isnull(a,0) then 'b'
when a is not null then 'a'
end as firstColumn,
case
when c > isnull(a,0) and c > isnull(b,0) then z
when b > isnull(a,0) then y
when a is not null then x
end as secondColumn
from table1 t1
join table2 t2 on t1.table2_id = t2.id
Just a bit of test data:
declare #table1 table (id int identity(1,1), a int, b int, c int, table2_id int default 1);
declare #table2 table (id int, x int, y int, z int);
insert into #table1 (a, b, c) values
(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1),
(4,5,5),(5,4,5),(5,5,4),(4,5,4),(4,4,5),(4,4,4),
(6,7,null),(6,null,7),(null,6,7),
(8,null,null),(null,8,null),(null,null,8),(null,null,null);
insert into #table2 (id, x, y, z) values (1,100,200,300);

The table structure you have shown us does not support your requirement. You need something that joins the rows in one table to the other.
If this exists you can then use (demo)
SELECT t1label,
t2col
FROM TableA TA
JOIN TableB TB
ON TA.rownum = TB.rownum
CROSS APPLY (SELECT TOP 1 *
FROM (VALUES('A', TA.a, TB.x),
('B', TA.b, TB.y),
('C', TA.c, TB.z)) v(t1label, t1col, t2col)
ORDER BY t1col DESC) CA
In the event a TableA row has two or more columns with the same maximum value it is undeterministic which will be selected.

Related

How to get columns from different table B based on condition of column on table A?

suppose i have two tables table A and table B
Table A
id name remarks
4 X XXX
6 Y YYY
7 Z ZZZ
Table B
id Aid remarks edit_flag
1 4 NULL 0
2 6 YY changes 1
3 7 Z cahnged 1
so, i would like to retrieve data like below:
if edit_flag is 1 (that is edited), get remarks column from table B else(edit_flag is 0) get remarks column from table A since it isnt edited
i am looking at something like this
if(edit_flag == 0)
then get remarks from table A
else get remarks from table B
so my result table should be looking like
Row_Counter remarks
1 XXX
2 YY changes
3 Z changed
Use CASE:
SELECT aID = a.id, name,
remarks = CASE b.edit_flag
WHEN 0 THEN a.remarks
WHEN 1 THEN b.remarks
END
FROM TableA a INNER JOIN TableB b ON a.id = b.Aid
You can join 2 tables and make a conditional query. Such as:
NOTE: I'm assuming that you're using a database supporting "iif". Otherwise you should use "case" as in the other answers.
Select a.id as id_id, iif(b.edit_flag = 0, a.remarks, b.remarks) as remark
from tableA as a inner join tableB as b on a.id=b.Aid
Use Case Statement
declare #tblA as table
(
id int,
name varchar(50),
remarks varchar(50)
)
insert into #tblA values(4,'x','xxx');
insert into #tblA values(6,'y','yyy');
insert into #tblA values(7,'z','zzz');
declare #tblB as table
(
id int,
Aid int,
remarks varchar(50),
edit_flag int
)
insert into #tblB values(1,4,NULL,0);
insert into #tblB values(2,6,'yy changes',1);
insert into #tblB values(3,7,'z changes',1);
SELECT
B.id,
B.Aid,
B.edit_flag,
CASE WHEN edit_flag=1 THEN B.remarks ELSE a.remarks END as remarks
FROM #tblB B
LEFT JOIN #tblA A ON B.Aid=A.id

Filter OUTER APPLY column in WHERE clause

I currently have a complex SQL query which is inserted into a temp table. The query includes an OUTER APPLY as not all returned records will apply to the result set.
I also need to use the OUTER APPLY columns in the WHERE clause to filter results but also include the results which do not apply into the OUTER APPLY .e. All Outer APPLY results = 1 and non-outer apply results.
This is a simple version of the query layout:
INSERT INTO #temp (X, Y, Z, O1, O2)
SELECT
X Y Z
FROM T1
INNER JOIN T2, T etc.
OUTER APPLY (
SELECT O1, O2 FROM XYZ…) OATable
WHERE
OATable.O1 = 1 -- I tried just adding “IN (1, NULL)” but this
still excludes the results.
Any help would be greatly appreciated.
Platform: SQL Server 2012+
Thank you
You can't compare directly to NULL because nothing equates to NULL (not even NULL itself). That precludes using IN here. Instead, just use an OR statement:
INSERT INTO #temp (X, Y, Z, O1, O2)
SELECT
X, Y, Z,
FROM T1
INNER JOIN T2 ON ...
OUTER APPLY (SELECT O1, O2 FROM XYZ…) OATable
WHERE
OATable.O1 = 1 OR OATable.O1 IS NULL
That assumes that O1 is a NOT NULL column in XYZ.
I think the answer from Tom H adresses the stated question
But I think this might be what you are actually looking for
SELECT X Y Z, OATable.*
FROM T1
INNER JOIN T2, T etc.
LEFT JOIN XYZ as OATable
on OATable.O1 = 1
In the answer from Tom you would need a literal OATable.O1 IS NULL (from my findings)
And that is not going to happen if the column is defined as not null
OATable.O1 IS NULL in an outer apply would only find literal value of null even if the column allows null
In this answer you get the left side with no match on OATable O1 = 1
Is this what you are trying to achieve:
CREATE TABLE #T1 ( X INT, y INT, z INT );
INSERT INTO #T1
( X, y, z )
VALUES ( 1 -- X - int
, 2 -- y - int
, 3 -- z - int
);
CREATE TABLE #t2 ( a INT, b INT );
INSERT INTO #t2
( a, b )
VALUES ( 2 -- a - int
, 5 -- b - int
);
CREATE TABLE #xyz ( a1 INT, a2 INT );
INSERT INTO #xyz
( a1, a2 )
VALUES ( 1 -- a1 - int
, 20 -- a2 - int
),
( NULL, 30 );
SELECT Insertdata.X
, Insertdata.y
, Insertdata.z
, Insertdata.a
, Insertdata.b
, Insertdata.a1
, Insertdata.a2
INTO #temp
FROM ( SELECT INQ.X
, INQ.y
, INQ.z
, INQ.a
, INQ.b
, O.a1
, O.a2
FROM ( SELECT X
, y
, z
, a
, b
FROM #T1
INNER JOIN #t2
ON y = a
) AS INQ
OUTER APPLY ( SELECT a1
, a2
FROM #xyz
) AS O
WHERE a1 = 1
OR a1 IS NULL
) AS Insertdata;
SELECT X
, y
, z
, a
, b
, a1
, a2
FROM #temp;

SQL joining on >=

I have a table like this in ORACLE
a b
-- --
1000 1
100 2
10 3
1 4
My other table has numbers like '67' or '112' in a column called numbers for example.
How can I join to this table using those values and get the correct result where >=1000 would be 1 and >= 100 would be 2 >=10 would be 3 etc.
I tried to do a
select g.b
from table o
join table2 g on o.column >= g.a
when I do this say 1002 was the value of g I would get the back these results.
1
2
3
4
when I just want 1
Easiest would be if your lookup table had ranges instead of just one number, such as row 1 = 1000,9999,1 and row 2 = 100,999,2 etc.
Then your join might be
SELECT OtherTable.n, lookup.b
from OtherTable
LEFT JOIN lookup on OtherTable.n between lookup.low and lookup.high
But, if you really want to use your original table, then on SQL Server, do this:
/*CREATE TABLE #A (a int,b int)
INSERT INTO #A VALUES (1000,1),(100,2),(10,3),(1,4)
CREATE TABLE #B (n INT)
INSERT INTO #B VALUES (67),(112),(4),(2001)
*/
SELECT B.n, A1.b
FROM #B B OUTER APPLY (SELECT TOP 1 a,b FROM #A A WHERE A.a<B.n ORDER BY A.a DESC) A1
Here's one way to do it using a subquery to get the MAX of column a, and then rejoining on the same table to get b:
select t.numericvalue, t2.b
from (
select t.numericvalue, max(t2.a) maxb
from table1 t
join table2 t2 on t.numericvalue >= t2.a
group by numericvalue
) t join table2 t2 on t.maxb = t2.a
SQL Fiddle Demo

CTE to recurse to root in Table A, and join results from Table B at each iteration

I'm new to CTE's in T-SQL but am loving them. However, I cannot get the logic right for this particular stored procedure I'm writing.
Given Table A with the columns:
Id Name Inherits ...
Where the column Inherits stores an int that is an id to another row in this same table.
And Table B with the columns:
Id Name AId ...
Where AId is a foreign key to a row in Table A.
How could one use a CTE to start from an arbitrary row (x) in A, collect all rows in B where AId = x.Id, and then recurse upwards in A by setting x to the row pointed to by x.Inherits. This should proceed until x.Inherits IS NULL.
So the overall effect is I want to return the related B rows for the starting A Id, and then all inherited B rows that we discover by examining the Inherits column of A recursively.
My thinking is to set up the CTE to recurse to the 'root' of A from an arbitrary row, and inside each recursive call have a JOIN to B. This is as far as I've got:
WITH c
AS
(
SELECT A.Inherits,A.Id, B.Name, 1 AS Depth
FROM tbl_A A
INNER JOIN tbl_B B ON B.AId = A.Id
WHERE A.Id = #ArbitraryStartingAId
UNION ALL
SELECT T.Inherits,T.Id, c.Name, c.Depth + 1 AS 'Level'
FROM tbl_A T
INNER JOIN c ON T.Id = c.Inherits
)
SELECT *
FROM c
Which produces:
1 4 b_val 1
11 1 b_val 2
NULL 11 b_val 3
Where b_val is taken from a Table B row (y) where #ArbitraryStartingAId = y.AId. The recursion on A is working, but not pulling in the correct corresponding B data upon each iteration.
If someone could help rewrite this to yield the results I need then that would be great.
Many thanks
Supposing we have
CREATE TABLE A ( Id int, Name nvarchar(10), Inherits int );
CREATE TABLE B ( Id int, Name nvarchar(10), AId int );
And some data:
INSERT A VALUES ( 1, 'one', 2 );
INSERT A VALUES ( 2, 'two', 3 );
INSERT A VALUES ( 3, 'three', null );
INSERT A VALUES ( 4, 'four', 3 );
INSERT B VALUES ( 1, 'B one', 1 );
INSERT B VALUES ( 2, 'B two', 2 );
INSERT B VALUES ( 3, 'B three', 3 );
INSERT B VALUES ( 4, 'B four', 4 );
We can build a CTE on A, and then join to B:
WITH cteA AS
(
-- Anchor
SELECT Id, Name, Inherits FROM A
WHERE Id = 1
UNION
-- Recursive
SELECT A.Id, A.Name, A.Inherits FROM A
INNER JOIN cteA ON A.Id = cteA.Inherits
)
SELECT * FROM cteA INNER JOIN B ON cteA.Id = B.AId;
to get (with the anchor at 1):
Id Name Inherits Id Name AId
----------- ---------- ----------- ----------- ---------- -----------
1 one 2 1 B one 1
2 two 3 2 B two 2
3 three NULL 3 B three 3
Here's where I'm at now and it's one possible answer as the correct data is returned:
WITH c
AS
(
SELECT A.Inherits,A.Id, B.Name, 1 AS Depth
FROM tbl_A A
INNER JOIN tbl_B B ON B.AId = A.Id
WHERE A.Id =#ArbitraryStartingAId
UNION ALL
SELECT T.Inherits,T.Id, T.Name, B.Name, c.Depth + 1 AS 'Level'
FROM tbl_A T
INNER JOIN tbl_B B ON B.AId = T.Id
INNER JOIN c ON T.Id = c.Inherits
)
SELECT DISTINCT *
FROM c ORDER BY Depth ASC
It feels a bit dirty though as it returns the root level B rows twice, hence the DISTINCT operator to filter these out...??

Problem combining result of two different queries into one

I have two tables (TableA and TableB).
create table TableA
(A int null)
create table TableB
(B int null)
insert into TableA
(A) values (1)
insert into TableB
(B) values (2)
I cant join them together but still I would like to show the result from them as one row.
Now I can make select like this:
select
(select A from tableA) as A
, B from TableB
Result:
A B
1 2
But if I now delete from tableB:
delete tableB
Now when I run the same query as before:
select
(select A from tableA) as A
, B from TableB
I see this:
A B
But I was expecting seeing value from tableA
like this:
Expected Result:
A B
1
Why is this happening and how can I still see the value from TableA although selectB is returning 0 rows?
I am using MS SQL Server 2005.
Use a LEFT JOIN (although it's more of a cross join in your case).
If your db supports it:
SELECT a.a, b.b
FROM a
CROSS JOIN b
If not, do something like:
SELECT a.a, b.b
FROM a
LEFT JOIN b ON ( 1=1 )
However, once you have more rows in a or b, this will return the cartesian product:
1 1
1 2
2 1
2 2
This will actually give you what you're looking for, but if you only have one row per table:
select
(select A from tableA) as A
, (select B from TableB) as B
give this a try:
DECLARE #TableA table (A int null)
DECLARE #TableB table (B int null)
insert into #TableA (A) values (1)
insert into #TableB (B) values (2)
--this assumes that you don't have a Numbers table, and generates one on the fly with up to 500 rows, you can increase or decrease as necessary, or just join in your Numbers table instead
;WITH Digits AS
(
SELECT 0 AS nbr
UNION SELECT 1 UNION SELECT 2 UNION SELECT 3
UNION SELECT 4 UNION SELECT 5 UNION SELECT 6
UNION SELECT 7 UNION SELECT 8 UNION SELECT 9
)
, AllNumbers AS
(
SELECT u3.nbr * 100 + u2.nbr * 10 + u1.nbr + 1 AS Number
FROM Digits u1, Digits u2, Digits u3
WHERE u3.nbr * 100 + u2.nbr * 10 + u1.nbr + 1 <= 500
)
, AllRowsA AS
(
SELECT
A, ROW_NUMBER() OVER (ORDER BY A) AS RowNumber
FROM #TableA
)
, AllRowsB AS
(
SELECT
B, ROW_NUMBER() OVER (ORDER BY B) AS RowNumber
FROM #TableB
)
SELECT
a.A,b.B
FROM AllNumbers n
LEFT OUTER JOIN AllRowsA a on n.Number=a.RowNumber
LEFT OUTER JOIN AllRowsB b on n.Number=b.RowNumber
WHERE a.A IS NOT NULL OR b.B IS NOT NULL
OUTPUT:
A B
----------- -----------
1 2
(1 row(s) affected)
if you DELETE #TableB, the output is:
A B
----------- -----------
1 NULL
(1 row(s) affected)
try this:
select a, (select b from b) from a
union
select b, (select a from a) from b
should retrieve you all the existing data.
you can filter it more by surrounding it with another select