Combine tables in select based on criteria in a column SQLServer - sql-server

I have two tables (each with about 20 columns), none of the column names match up but some of the values in 1 column match the values in another (see below).
I want to get a the combination of the 2 tables on certain columns based on True/False values in a column on the primary table.
I am doing all of this using the SQLServer Third Party JDBC Drivers in Oracle's SQL Developer (I am not sure if or how that might have an effect on my syntax).
I am sure that this is simple, but I cannot find any examples that do this and I am just too new to SQL to wrap my head around it.
CREATE TABLE [dbo].[TableA] (
[colA1] VARCHAR (10) NULL,
[colA2] VARCHAR (10) NULL,
[colA3] VARCHAR (10) NULL,
[colA4] VARCHAR (10) NULL,
[colA5] VARCHAR (10) NULL,
[colA6] INT NULL,
[colKey] INT NOT NULL,
CONSTRAINT [PK_TableA] PRIMARY KEY CLUSTERED ([colKey] ASC)
);
CREATE TABLE [dbo].[TableB] (
[colB1] VARCHAR (10) NULL,
[colB2] VARCHAR (10) NULL,
[colB3] VARCHAR (10) NULL,
[colB4] INT NULL,
[colKey] INT NOT NULL,
PRIMARY KEY CLUSTERED ([colKey] ASC)
);
INSERT INTO TableA(colKey,colA1,colA2,colA3,colA4,colA5,colA6) VALUES (1,'AC1-1','AC2-1','AC3-1',NULL,'FALSE',2016);
INSERT INTO TableA(colKey,colA1,colA2,colA3,colA4,colA5,colA6) VALUES (2,'AC1-2','AC2-2','AC3-2',NULL,'FALSE',2016);
INSERT INTO TableA(colKey,colA1,colA2,colA3,colA4,colA5,colA6) VALUES (3,'AC1-3',NULL,NULL,'AC4-3','TRUE',2016);
INSERT INTO TableA(colKey,colA1,colA2,colA3,colA4,colA5,colA6) VALUES (4,'AC1-4',NULL,NULL,'AC4-4','TRUE',2016);
INSERT INTO TableA(colKey,colA1,colA2,colA3,colA4,colA5,colA6) VALUES (5,'AC1-5','AC2-5','AC3-5',NULL,'FALSE',2015);
INSERT INTO TableA(colKey,colA1,colA2,colA3,colA4,colA5,colA6) VALUES (6,'AC1-6','AC2-6','AC3-6',NULL,'FALSE',2015);
INSERT INTO TableA(colKey,colA1,colA2,colA3,colA4,colA5,colA6) VALUES (7,'AC1-7',NULL,NULL,'AC4-7','TRUE',2015);
INSERT INTO TableA(colKey,colA1,colA2,colA3,colA4,colA5,colA6) VALUES (8,'AC1-8',NULL,NULL,'AC4-8','TRUE',2015);
INSERT INTO TableA(colKey,colA1,colA2,colA3,colA4,colA5,colA6) VALUES (9,'AC1-9',NULL,NULL,'AC4-9','TRUE',2016);
INSERT INTO TableB(colKey,colB1,colB2,colB3,colB4) VALUES (1,'AC4-3','BC2-1','BC3-1',2015);
INSERT INTO TableB(colKey,colB1,colB2,colB3,colB4) VALUES (2,'AC4-4','BC2-2','BC3-2',2015);
INSERT INTO TableB(colKey,colB1,colB2,colB3,colB4) VALUES (3,'AC4-4','BC2-3','BC3-3',2016);
INSERT INTO TableB(colKey,colB1,colB2,colB3,colB4) VALUES (4,'AC4-3','BC2-4','BC3-4',2016);
INSERT INTO TableB(colKey,colB1,colB2,colB3,colB4) VALUES (5,'AC4-7','BC2-5','BC3-5',2015);
INSERT INTO TableB(colKey,colB1,colB2,colB3,colB4) VALUES (6,'AC4-8','BC2-6','BC3-6',2015);
TableA
+-------+--------+--------+--------+-------+-------+
| colA1 | colA2 | colA3 | colA4 | colA5 | colA6 |
+-------+--------+--------+--------+-------+-------+
| AC1-1 | AC2-1 | AC3-1 | (Null) | FALSE | 2016 |
| AC1-2 | AC2-2 | AC3-2 | (Null) | FALSE | 2016 |
| AC1-3 | (Null) | (Null) | AC4-3 | TRUE | 2016 |
| AC1-4 | (Null) | (Null) | AC4-4 | TRUE | 2016 |
| AC1-5 | AC2-5 | AC3-5 | (Null) | FALSE | 2015 |
| AC1-6 | AC2-6 | AC3-6 | (Null) | FALSE | 2015 |
| AC1-7 | (Null) | (Null) | AC4-7 | TRUE | 2015 |
| AC1-8 | (Null) | (Null) | AC4-8 | TRUE | 2015 |
| AC1-9 | (Null) | (Null) | AC4-9 | TRUE | 2016 |
+-------+--------+--------+--------+-------+-------+
TableB
+-------+-------+-------+-------+
| colB1 | colB2 | colB3 | colB4 |
+-------+-------+-------+-------+
| AC4-3 | BC2-1 | BC3-1 | 2015 |
| AC4-4 | BC2-2 | BC3-2 | 2015 |
| AC4-4 | BC2-3 | BC3-3 | 2016 |
| AC4-3 | BC2-4 | BC3-4 | 2016 |
+-------+-------+-------+-------+
Results Table
+-------+--------+-------+-------------------------+-------------------------+
| colA1 | colA4 | colA5 | combined(colA2 & colB2) | combined(colA3 & colB3) |
+-------+--------+-------+-------------------------+-------------------------+
| AC1-1 | (null) | FALSE | AC2-1 | AC3-1 |
| AC1-2 | (null) | FALSE | AC2-2 | AC3-2 |
| AC1-3 | AC4-3 | TRUE | BC2-1 | BC3-1 |
| AC1-4 | AC4-4 | TRUE | BC2-2 | BC3-2 |
| AC1-9 | AC4-9 | TRUE | (null) | (null) |
+-------+--------+-------+-------------------------+-------------------------+
So I think I need some kind of SELECT like this:
SELECT colA1, colA5,
IF colA5 = True
THEN colB2, colB3, etc.
ELSE colA2, ColA3, etc.
FROM tableB, tableA
WHERE colA1 = colB1 AND colB4 = 2016 AND colA6 = 2016
I have tried this:
SELECT A.colA1
,A.colA4
,A.colA5
,CASE
WHEN A.colA5 = TRUE
THEN B.colB2
ELSE A.colA2
END AS 'combined(colA2 & colB2)'
,CASE
WHEN A.colA5 = TRUE
THEN B.colB3
ELSE A.colA4
END AS 'combined(colA3 & colB3)'
,
FROM TableA A
,TableB B
WHERE A.colA6 = '2016'
AND B.colB4 = '2016'
AND (
A.colA4 = B.colB1
OR A.colA4 IS NULL
)
what I get is this:
+-------+-------+-------+-------------------------+-------------------------+
| colA1 | colA4 | colA5 | combined(colA2 & colB2) | combined(colA3 & colB3) |
+-------+-------+-------+-------------------------+-------------------------+
| AC1-3 | AC4-3 | TRUE | BC2-1 | BC3-1 |
| AC1-4 | AC4-4 | TRUE | BC2-2 | BC3-2 |
+-------+-------+-------+-------------------------+-------------------------+
So I am missing the rows were TableA/colA5 are FALSE. Also, I need 12 of these "combined" columns, is there a way that I can avoid using 12 CASE statements?

After learning about joins and case here is the answer (though apparently I will have to use the 12 CASE statements that I would have preferred to avoid).
SELECT A.colA1
,A.colA4
,A.colA5
,CASE
WHEN A.colA5 = 'TRUE'
THEN B.colB2
ELSE A.colA2
END AS 'combined(colA2 & colB2)'
,CASE
WHEN A.colA5 = 'TRUE'
THEN B.colB3
ELSE A.colA3
END AS 'combined(colA3 & colB3)'
FROM TableA A LEFT JOIN TableB B ON A.colA4 = B.colB1
WHERE (A.colA6 = '2016' and A.colA5 ='FALSE')
or (A.colA6 = '2016' and A.colA5 ='true' and B.colB4 = '2016')
or (A.colA6 = '2016' and A.colA5 ='true' and B.colB4 is null)
;

Related

T-SQL Limited Cross Join

I want to join 2 tables such that I get the NAR for every combination of Type and BillingID where it exists.
Where a BillingID doesn't have a certain Type, then either NULL or 0 is returned for the NAR along with the Type and BillingID.
Is something like this even possible using SQL?
A simplified version of my data is shown below:
Type list:
+----------+
| Type |
+----------+
| NEW |
| CHNG |
| LAP |
+----------+
Data:
+----------+-----------+-----+
| Type | BillingID | NAR |
+----------+-----------+-----+
| NEW | ABC | 5 |
| CHNG | ABC | 15 |
| LAP | ABC | 10 |
| CHNG | DEF | 20 |
+----------+-----------+-----+
Desired result:
+----------+-----------+-----+
| Type | BillingID | NAR |
+----------+-----------+-----+
| NEW | ABC | 5 |
| CHNG | ABC | 15 |
| LAP | ABC | 10 |
| CHNG | DEF | 20 |
| NEW | DEF | 0 |
| LAP | DEF | 0 |
+----------+-----------+-----+
The last 2 rows are what is causing me problems.
I think you can do it like this:
declare #table table (type1 varchar(5))
insert into #table
values
('new'),
('chng'),
('lap')
declare #table2 table (typeid varchar(5),billingid varchar(5),nar int)
insert into #table2
values
( 'NEW', 'ABC', 5 ),
( 'CHNG' , 'ABC', 15 ),
( 'LAP' , 'ABC', 10 ),
( 'CHNG' , 'DEF', 20 )
select Z.*,case when c.nar IS null then 0 else c.nar end as nar from (
select * from #table a
outer apply (select distinct billingid from #table2 b ) p
)Z
left join #table2 c on Z.type1 = c.typeid and Z.billingid = c.billingid
order by billingid
Result

find rows in table1 that are not in table2 based on comparing two columns

I have two tables that have 3 columns. I want to compare two columns in both tables to find the rows that are in the first one but not the other. So far I am using a LEFT JOIN on two columns. I tested on a temp tables with junk data and the method works but my production data set has a lot of rows so I cannot validate if it actually works.
CREATE TABLE table1(
[Timestamp] [date],
[name] [varchar](50) NULL,
[value] [varchar](50) NULL
)
CREATE TABLE table2(
[Timestamp] [date],
[name] [varchar](50) NULL,
[value] [varchar](50) NULL
)
.
INSERT INTO table1 VALUES ('2017-09-10', 'a', 'one'),
('2017-01-19', 'a', 'two'),
('2016-12-07', 'a', 'three'),
('2016-11-15', 'b', 'four'),
('2016-10-19', 'b', 'five'),
('2017-07-09', 'b', 'six'),
('2016-01-31', 'c', 'seven'),
('2016-12-15', 'd', 'eight');
INSERT INTO table2 VALUES ('2016-12-15', 'a', 'two'),
('2016-06-20', 'b', 'four'),
('2017-04-09', 'b', 'five'),
('2016-03-03', 'b', 'six'),
('2016-03-24', 'c', 'seven'),
('2016-07-04', 'e', 'nine'),
('2016-10-24', 'f', 'ten'),
('2016-11-06', 'g', 'eleven')
.
SELECT * FROM table1
| Timestamp | name | value |
|------------|------|-------|
| 2017-09-10 | a | one |
| 2017-01-19 | a | two |
| 2016-12-07 | a | three |
| 2016-11-15 | b | four |
| 2016-10-19 | b | five |
| 2017-07-09 | b | six |
| 2016-01-31 | c | seven |
| 2016-12-15 | d | eight |
SELECT * FROM table2
| Timestamp | name | value |
|------------|------|--------|
| 2016-12-15 | a | two |
| 2016-06-20 | b | four |
| 2017-04-09 | b | five |
| 2016-03-03 | b | six |
| 2016-03-24 | c | seven |
| 2016-07-04 | e | nine |
| 2016-10-24 | f | ten |
| 2016-11-06 | g | eleven |
.
SELECT table1.name, table1.value, IIF(table2.Timestamp IS NULL, 'missing', 'not missing') AS 'status' FROM table1 LEFT JOIN table2 ON table1.name = table2.name AND table1.value = table2.value
| name | value | status |
|------|-------|-------------|
| a | one | missing |
| a | two | not missing |
| a | three | missing |
| b | four | not missing |
| b | five | not missing |
| b | six | not missing |
| c | seven | not missing |
| d | eight | missing |
.
DROP TABLE table1
DROP TABLE table2
You're on the right track. You just needed to test for the right side value to be null to determine if there was no match. For best performance you'll want to index on name & value in both tables.
SELECT table1.name,
table1.value
FROM table1
LEFT OUTER JOIN table2
ON table1.name = table2.name
AND table1.value = table2.value
WHERE table2.value IS NULL

how to retrieve unmatched data from two table

I created a table managers :
create table managers(
ManagerId int identity(1,1) not for replication not null,
M_name varchar(20),
Salary varchar(20),
joining_year varchar(20),
city varchar(20),
IdCode int
)
then insert some data into this table:
ManagerId | M_name | Salary | joining_year | city | IdCode
----------------------------------------------------------------------
1 | riva | 50000 | 1998 | pune | 4
2 | tanmay | 48500 | 1990 |gurgaon | 2
3 | david | 49000 | 2001 | goa | 2
4 | null | null | null | null | null
5 | null | null | null | null | null
6 | dannial | 52185 | 2010 | kanpur | 6
And have second table managerEmp
create table managerEmp(
employeId int identity(1,1) not for replication not null,
family_member varchar(20),
wife_name varchar(20),
age int
)
I have some data in that table:
employeId | family_member |wife_name | age
--------------------------------------------
1 | 6 |mrs.kapoor| 31
2 | 5 |mrs.mishra| 25
3 | null |nll | null
4 | 2 |mrs.khan | 21
5 | 4 |mrs.bajaj | 22
Now, I want to select uncommon data from that table. The result would be:
M_name | Salary | city | wife_name | age
-----------------------------------------
null | null | null | mrs.khan | 21
null | null | null | mrs.bajaj | 22
dannial| 52185 |kanpur| null |null
Query based on your output:
SELECT M_name,Salary,city,wife_name,age FROM managers LEFT JOIN managerEmp
ON managers.ManagerId =managerEmp.employeId
WHERE M_name is NULL OR employeId IS NULL
Link to SQL fiddle http://sqlfiddle.com/#!6/e1776/1

Query a query's column name in SQL Server

I want to query in SQL Server a column's name. I know it is possible to get a table's columns from the system table, but unfortunately that's not enough for me.
Example:
I have a table that contains an ID column and a string column. The table's name is test, and it has a testID and a test column.
This query:
select column_name
from information_schema.columns
where table_name = 'teszt'
return the names of the columns of my table. So it returns testID and Test.
What I want is when I use a query like this:
select count(*) as Amount from test
I want a query that can return the column names of my query. So in this specific case it returns the string 'Amount'. I don't know if that is possible.
Not sure if there is an easier way of getting the name of columns with aliases, but one way of doing it is via XML. This query will return one row per column in the inner query:
select T1.res.value('local-name(.)', 'varchar(50)')
from (select cast(
(
select count(*) as Amount from test
for xml raw) as xml
)) q(res)
CROSS APPLY q.res.nodes('/row/#*') as T1(res)
In SQL Server 2012 you have a stored procedure that you can use for exactly this purpose.
sp_describe_first_result_set (Transact-SQL)
SQL Fiddle
MS SQL Server 2012 Schema Setup:
create table test(id int);
Query 1:
exec sp_describe_first_result_set N'select count(*) as Amount from test'
Results:
| IS_HIDDEN | COLUMN_ORDINAL | NAME | IS_NULLABLE | SYSTEM_TYPE_ID | SYSTEM_TYPE_NAME | MAX_LENGTH | PRECISION | SCALE | COLLATION_NAME | USER_TYPE_ID | USER_TYPE_DATABASE | USER_TYPE_SCHEMA | USER_TYPE_NAME | ASSEMBLY_QUALIFIED_TYPE_NAME | XML_COLLECTION_ID | XML_COLLECTION_DATABASE | XML_COLLECTION_SCHEMA | XML_COLLECTION_NAME | IS_XML_DOCUMENT | IS_CASE_SENSITIVE | IS_FIXED_LENGTH_CLR_TYPE | SOURCE_SERVER | SOURCE_DATABASE | SOURCE_SCHEMA | SOURCE_TABLE | SOURCE_COLUMN | IS_IDENTITY_COLUMN | IS_PART_OF_UNIQUE_KEY | IS_UPDATEABLE | IS_COMPUTED_COLUMN | IS_SPARSE_COLUMN_SET | ORDINAL_IN_ORDER_BY_LIST | ORDER_BY_IS_DESCENDING | ORDER_BY_LIST_LENGTH | TDS_TYPE_ID | TDS_LENGTH | TDS_COLLATION_ID | TDS_COLLATION_SORT_ID |
|-----------|----------------|--------|-------------|----------------|------------------|------------|-----------|-------|----------------|--------------|--------------------|------------------|----------------|------------------------------|-------------------|-------------------------|-----------------------|---------------------|-----------------|-------------------|--------------------------|---------------|-----------------|---------------|--------------|---------------|--------------------|-----------------------|---------------|--------------------|----------------------|--------------------------|------------------------|----------------------|-------------|------------|------------------|-----------------------|
| 0 | 1 | Amount | 1 | 56 | int | 4 | 10 | 0 | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | 0 | 0 | 0 | (null) | (null) | (null) | (null) | (null) | 0 | (null) | 0 | 0 | 0 | (null) | (null) | (null) | 38 | 4 | (null) | (null) |
Maybe you want something like this? :-)
SELECT AMOUNT
FROM
(
SELECT COUNT(*) AS AMOUNT
FROM TEST
)X

Flatten a recordset in SQL Server?

Say you get a recordset like the following:
| ID | Foo | Bar | Red |
|-----|------|------|------|
| 1 | 100 | NULL | NULL |
| 1 | NULL | 200 | NULL |
| 1 | NULL | NULL | 300 |
| 2 | 400 | NULL | NULL |
| ... | ... | ... | ... | -- etc.
And you want:
| ID | Foo | Bar | Red |
|-----|-----|-----|-----|
| 1 | 100 | 200 | 300 |
| 2 | 400 | ... | ... |
| ... | ... | ... | ... | -- etc.
You could use something like:
SELECT
ID,
MAX(Foo) AS Foo,
MAX(Bar) AS Bar,
MAX(Red) AS Red
FROM foobarred
GROUP BY ID
Now, how might you accomplish similar when Foo, Bar, and Red are VARCHAR?
| ID | Foo | Bar | Red |
|-----|----------|---------|---------|
| 1 | 'Text1' | NULL | NULL |
| 1 | NULL | 'Text2' | NULL |
| 1 | NULL | NULL | 'Text3' |
| 2 | 'Test4' | NULL | NULL |
| ... | ... | ... | ... | -- etc.
To:
| ID | Foo | Bar | Red |
|-----|----------|---------|---------|
| 1 | 'Text1' | 'Text2' | 'Text3' |
| 2 | 'Text4' | ... | ... |
| ... | ... | ... | ... | -- etc.
Currently working primarily with SQL Server 2000; but have access to 2005 servers.
The query you had above works just fine for VARCHAR fields as it did for INT fields. The problem with your query though is that if you have two rows with the same ID, and both of those rows had something in the "Foo" column, then only the one with the highest value (both for INT and VARCHAR) will be displayed.
I don't have access to a SQL2K box at the minute but select max(column) will work on nvarchars in 2005. The only problem will be if you have multiple text values under each column for each id in your original table...
CREATE TABLE Flatten (
id int not null,
foo Nvarchar(10) null,
bar Nvarchar(10) null,
red Nvarchar(10) null)
INSERT INTO Flatten (ID, foo, bar, red) VALUES (1, 'Text1', null, null)
INSERT INTO Flatten (ID, foo, bar, red) VALUES (1, null, 'Text2', null)
INSERT INTO Flatten (ID, foo, bar, red) VALUES (1, null, null, 'Text3')
INSERT INTO Flatten (ID, foo, bar, red) VALUES (2, 'Text4', null, null)
SELECT
ID,
max(foo),
max(bar),
max(red)
FROM
Flatten
GROUP BY ID
returns
ID Foo Bar Red
----------- ---------- ---------- ----------
1 Text1 Text2 Text3
2 Text4 NULL NULL

Resources