SQL Creating multiple columns in View from one single table-column - sql

I'm trying to create a View in SQL Server 2008 R2, where data is extracted from two tables with dual one-to-one relationships, and I want to create two columns in the view based values from a single column in one of the tables.
The tables are currently similar to these examples:
TableA:
PrimaryKey | Name | Value_FK | Number_FK
-------------------------------------------
66 | NameA | 1 | 2
77 | NameB | 3 | 4
TableB:
PrimaryKey | Value
-------------------
1 | 238
2 | 456
3 | 100
4 | 200
The View should look like this:
Name | Value | Number
-------------------------
NameA | 238 | 456
NameB | 100 | 200
('Value' and 'Number' are essentially the same type, and are both found in the 'Value' column in TableB. I thought it would be more easy to distingiush between 'Value' and 'Number', than 'ValueA' and 'ValueB').
The factor that should decide which values go into Column 'Value' or Column 'Number' is the PrimaryKey in TableB and its references in either foreignkey in TableA (but both FKs shall NEVER refer to the same key).
This is likely not the most brilliant database model, having dual relationships between to tables. This is however due to mapping some C#.NET classes to a database by using ADO.NET Entity Framework, where Class A has two objects of Class B (in this case named 'Value' and 'Number'), and the database model currently constructs two relationships becuase of this. Changing this is not an option.
I've tried googling this, but I find it difficult to find an answer that I need. Especially when most results are about the opposite: Selecting multiple columns into one column.
So, how should I write the Select statement?
CREATE VIEW ViewName
AS
SELECT DISTINCT a.Name as 'Name', ????? as 'Value', ????? as 'Number'
FROM TableA a, TableB b
I am quite rusty with advanced SQL-commands. It's been over 1,5 years since last I was into something this advanced. I tried something similar to this first:
CREATE VIEW ViewName
AS
WITH Name AS
( SELECT DISTINCT a.Name FROM TableA a )
Value AS
(
SELECT DISTINCT b.Value as 'Value' FROM TableA a, TableB b
WHERE b.PrimaryKey = an.ValueA_FK
),
Number AS
(
SELECT DISTINCT b.Value as 'Number'
FROM TableA a, TableB b
WHERE a.PrimaryKey = an.ValueB_PrimaryKey
)
SELECT DISTINCT
* FROM Name, Value, Number
The result of my utterly failed attempt is like this:
Name | Value | Number
-------------------------
NameA | 100 | 200
NameB | 100 | 200
NameA | 100 | 456
NameB | 100 | 456
NameA | 238 | 200
NameB | 238 | 200
NameA | 238 | 456
NameB | 238 | 456
Now, any suggestiong as what to fill in the query?

You can reference the same table more than once in the FROM clause:
SELECT a.Name as 'Name', b1.Value as 'Value', b2.Value as 'Number'
FROM TableA a
inner join TableB b1
on
a.Value_FK = b1.PrimaryKey
inner join TableB b2
on
a.Number_FK = b2.PrimaryKey
I've also removed the DISTINCT, since it shouldn't be your habit to add one, and there's nothing in the question that suggests one is necessary. I've also used ANSI-Style joins. These are almost always to be preferred over the older style (where tables in the FROM clause are just separated by commas)
If it's possible that some rows in TableA have a NULL Value_FK or Number_FK, and you still want those rows in your view, you would switch one or both of the inner joins to be left joins. You would then also decide whether the output column should be NULL (in which case you're done) or some other value (in which case, you would have e.g. COALESCE(b1.Value,<Value when null>) as 'Value').

This query will yield results you want:
select t1.Name, value.Value, number.Value as Number
from TableA t1
inner join TableB value on value.PrimaryKey = t1.Value_FK
inner join TableB number on number.PrimaryKey = t1.Number_FK
you over-complicated your query a lot.

Related

Left Join with Distinct Right table returns more rows in SQL Server

I have two tables, one is my Main table and one is Mapping table. The simplified tables look like this :
Main :
VALUE | CUSTNAME
123 | ADELE
323 | GORTH
242 | GORTH
345 | VIX
...
Mapping :
ISSUER | CATEGORY
ADELE | A
GORTH | B
DENN | B
VIX | C
...
What I want to do is add a CATEGORY column to my Main table, so I to use a left join, but somehow it returns more rows than I originally have. And I can't check it one by one because it has around 30000 records. The increase it self is miniscule, 40 records, but it still a difference and it shows.
Things to note and things I already tried :
Main table can hold the same CUSTNAME while Mapping table is distinct, making it many-to-one relation.
Mapping table holds duplicate records, so I tried to select distinct it first before joining it
I already checked the NULL to see if the Mapping table miss anything, but it doesn't seems to be the case
I use both inner join and left join but it return the same result
So what did I do wrong here and how can I fix it?
My query :
SELECT A.*, B.CATEGORY AS CATEGORY
FROM Main A
LEFT JOIN
(SELECT DISTINCT * FROM Mapping) B ON A.CUSTNAME = B.Issuer
My output right now :
VALUE | CUSTNAME | CATEGORY
123 | ADELE | A
323 | GORTH | B
242 | GORTH | B
345 | VIX | C
... with extra 40 records
My expected output :
VALUE | CUSTNAME | CATEGORY
123 | ADELE | A
323 | GORTH | B
242 | GORTH | B
345 | VIX | C
... without extra 40 records
You can do it as follows if you are looking to eliminate duplicates Mapping.Issuer ONLY.
SELECT A.*, B.CATEGORY AS CATEGORY
FROM Main A
LEFT JOIN
(SELECT Issuer, MAX(CATEGORY) AS CATEGORY FROM Mapping group by Issuer) B ON A.CUSTNAME = B.Issuer
Probably you have data on Mapping table like :
insert into Mapping values
('ADELE','A'),
('GORTH','B'),
('DENN','B'),
('VIX','C'),
('VIX','D');
That means ('VIX','C'), ('VIX','D') are not duplicates
demo here
This:
SELECT DISTINCT * FROM Mapping
won't prevent duplicates. Distinct * means that the tuple(combination) of ALL columns will be considered as unique; However, if there are two rows with same Issuer but a different value in any of the other columns, it will still return multiple rows. Like this:
Issuer | ManufactureId
5623 894
5623 895
This, in turn, will make one row from A turn into multiple from the left join.

Using self join on a table to compare two columns based on a linked column in the same table

I have the following:
TableA
ID | DocumentType | DocumentCode | DocumentDate | Warehouse | RefecenceCode
---+--------------+--------------+--------------+-----------+--------------
1 | DeliveryNote | DOC-001 | 2017-04-21 | 1 | NULL
2 | Invoice | DOC-002 | 2017-04-21 | 2 | DOC-001
As you can see, the warehouse is different on each document and DOC-002 is related to DOC-001 through the information in ReferenceCode column (which means that was created starting from DOC-001 as a source document).
It is supposed for the DOC-002 to have the same information but sometimes might be different and in this case, I was tried to create a query (I think self join applies here) in order to check what information is different in the DOC-002 in this case compared to DOC-001, based on the reference code, but I couldn't managed to do it.
If someone could give me a hand, I'll be very grateful.
This is the SQL query:
select *
from TableA tbl
inner join TableA tbla on tbl.id = tbla.id
where tbla.ReferenceCode = tbl.DocumentCode
You indeed want to join the table to itself. But joining on the ID column won't work, because that column doesn't relate records to each other. Instead, you need to join on the DocumentCode and ReferenceCode fields. Then only include the records that have some difference (in this case, I'm only comparing the DocumentDate and Warehouse fields).
select tbla.*
from TableA tbl
join TableA tbla on tbl.DocumentCode = tbla.ReferenceCode
where tbla.DocumentDate != tbl.DocumentDate
or tbla.Warehouse != tbl.Warehouse

How to SQL JOIN on concatenated columns in MS Access?

I have 2 tables where column X and Y are concatenated to represent a unique identifier. I want to find all rows in tableB that do not exist in tableA and add them into tableC.
-------tableA-------- // tableA is a master refernce table with all names so far
|__X__|__Y__|_name__|
| 3 | 7 | Mary |
| 3 | 2 | Jaime |
-------tableB-------- // tableB is an input file with all daily names (some repeats already exist in tableA)
|__X__|__Y__|_name__|
| 2 | 5 | Smith |
| 3 | 7 | Mary |
-------tableC-------- // tableC is a temporary holding table for new names
|__X__|__Y__|_name__|
| | | |
DESIRED RESULT:
-------tableC-------- // tableB - tableA = tableC
|__X__|__Y__|_name__|
| 2 | 5 | Smith |
I want to match rows based on a concatenated X+Y value. My SQL query so far looks like this:
INSERT INTO tableC
SELECT * FROM tableA
LEFT JOIN tableB
ON tableA.X & table.B = tableB.X & tableB.Y
WHERE tableB.X & tableB.Y IS null
However, this does not give me the intended result. I cannot use EXISTS as my actual data set is very big. Could anyone give me suggestions?
I don't think the slowness is caused by exists. Your query is probably slow because you're trying to use concatenation to match multiple columns. Use and instead and make sure you have a composite index on (x,y):
This will select all unique rows in tableB that don't have the same (x,y) value in tableA. Note that any rows with the same x,y but a different name will show up in the result (i.e. 2,5,Joe would also appear). If you don't want that, then you have to group by x,y and decide which name you want in case of duplicate x,y but different name.
select distinct x,y,name
from tableB b
where not exists (
select 1 from tableA a
where a.x = b.x
and a.y = b.y
)

Difference between "NOT IN table.col" and "NOT IN SELECT col FROM table"

A pretty basic question. But what is the difference between
SELECT t.col
FROM table t, other_table o
WHERE t.col NOT IN o.col
and
SELECT col
FROM table
WHERE col NOT IN (SELECT col FROM other_table)
Semantically this sounds pretty equal to me, but the first one creates duplicates. What am I understanding wrong?
The first one won't even run in most RDBMS, but in oracle it returns every combination of records except where t.col = o.col, you'd see this if you added o.col to your SELECT
The latter query returns records from table that don't share the col value with any records in other_table.
Best illustrated by example:
Table1
| ANIMAL |
|--------|
| dog |
| cat |
| horse |
Table2
| ANIMAL |
|--------|
| dog |
| fish |
Queries:
SELECT t."animal",o."animal"
FROM Table1 t, Table2 o
WHERE t."animal" NOT IN o."animal"
| ANIMAL | ANIMAL2 |
|--------|---------|
| cat | dog |
| horse | dog |
| dog | fish |
| cat | fish |
| horse | fish |
SELECT t."animal"
FROM Table1 t
WHERE t."animal" NOT IN (SELECT o."animal" FROM Table2 o)
| ANIMAL |
|--------|
| horse |
| cat |
Demo: SQL Fiddle
Basically, you've got a cartesian product in the first query which would return every combination of records from the two tables, but your WHERE criteria filters out one of them. The second query has no JOIN, implicit/explicit, it's just taking records from one table and filtering based on criteria that happens to draw from another table.
As far as I know, the query (slightly modified):
SELECT t.col
FROM table t, other_table o
WHERE t.col <> o.col
makes a cartesian product, then filters it.
The below example might not be the exact process that takes place, but it might give an abstract overview of the situation.
If in table table you would have following rows:
col
----
A
B
and in table other_table there would be:
col
---
B
E
cartesian product (FROM table t, other_table o) of the two tables query would probably be:
table.col other_table.col
---------------------------
A B
A E
B B
B E
Then, applying the WHERE t.col <> o.col clause the above would be filtered, giving the results
table.col other_table.col
---------------------------
A B
A E
B E
Since in the query result set, there is only table.col chosen for the output, the final result contains A value duplicates:
table.col
---------
A
A
B
I hope it could help you some way.
# UPDATE
As for the query:
SELECT col
FROM table
WHERE col NOT IN (SELECT col FROM other_table)
Since there is no join, only the row set from the table table is taken into account when building the result.
As far as I understand well, the condition WHERE col NOT IN (SELECT col FROM other_table) is evaluated against each row from the table.
Column table.col is checked whether it belongs to the result set returned by the subquery taking the data from other_table. If it validates to TRUE, then, it's taken into result set, if not, it's excluded from it.
Summing it up, I think that the first query doubles the table.col values only because of the preparing phase, where the tables are joined (merged) together, thus second query takes to the result set only records from table using other_table only for validation purposes. That is implicated from the query structure - if I'm right of course.

SQL inner join two tables with the same column names

I have two tables with a variable amount of columns. (I don't know how many columns or what there names will be) for example Table A and Table B.
TableA:
ID | B_ID | {variable}
TableB
ID | {variable}
Query:
SELECT TableA.*, TableB.* FROM TableA INNER JOIN TableB ON TableA.B_ID= TableB.id;
When TableA and TableB both have a column with a same name I can't distinguish between the two different columns. For example of both tables has the column "Name" this query would result in :
ID | ID | B_ID | NAME | NAME |
1 | 35 | 35 | bob | jim |
What I am looking for is a way to differentiate between the two tables. Preferably with a prefex for the column names such as.
TableA_ID | TableB_ID | TableA_B_ID | TableA_NAME | TableB_NAME |
1 | 35 | 35 | bob | jim |
I know of the "AS" keyword but the problem is that I don't know what the column names are going to be before hand. (I don't know if TableA or TableB are going to have the column Name)
So my question is
How do you differentiate the columns between the two tables with a INNER JOIN when the tables may have the same column names ?
I am using SQLite3.
Your result set (given your query) should have all of the TableA columns followed by all the TableB colums, so when you get to the second ID colum, you know you're into the TableB data.
That said, it is would seem odd to me that you're querying all the data out of two tables about which you know functionally nothing...
This is admittedly a hack solution, but this:
SELECT TableA.*, "#", TableB.*
FROM TableA INNER JOIN TableB ON TableA.B_ID= TableB.id;
Would produce a list of results which would be divided in two blocks, left and right of the # column.