Join two Tables with one have columns to rows - sql

I have a table with this structure:
Table 1: ID(PK) | <other columns>
1 | otherdata
2 | otherdata
the other table, It´s a list of documents (PDF,DOC,etc) with a URL to download. these documents is stored in my network.
Table 2: ID | IDDOC | LINKDOC | INFO
1 | 1 | 'http://URL1' | 'Info1'
1 | 2 | 'http://URL2' | 'Info2'
2 | 1 | 'http://URL3' | 'Info3'
ID is the foreign key for Table 1,IDDOC is a foreign key to a 3rd table (below) that describe the document type:
Table 3: IDDOC | Name
1 | 'Contract'
2 | 'Notification'
I need to generate a query to join these tables and get a similar structure
ID | <SomeCollumsTable1> | NameDesc1 | NameURL1 | ... | NameDesc2 | NameURL2
Example output:
ID | <SomeCollumsTable1> | ContractDesc | ContractURL | NotificationDesc | NotificationURL
1 | otherdata | 'Info1' | 'http://URL1' | 'Info2' | 'http://URL2'
2 | otherdata | 'Info3' | 'http://URL3' | '' | ''
I.E. Generate many pairs Desc/URL as many records exits in "Table3". the sample data have 2 documents types and generate 4 columns.
Currently i have subquerys to each desired document, but sounds very inefficient for me, the query is big and new documents i add in "Table3" need change in the whole query and need to just adjust the Where clause to indicate why IDDOC´s need. (using a IN clause)
Or its better to manipulate this in my application (winforms/vb.net)?
The App generate a report in EXCEL format.

Please try the below query:
DECLARE #qu NVARCHAR(MAX), #pcol NVARCHAR(MAX)
SELECT #pcol= COALESCE(#pcol + ',','') + type FROM
(SELECT Name+N'URL' AS type FROM t3 UNION SELECT Name+N'Desc' AS type FROM t3 ) A
SET #qu=N'Select ID,b,c,'+ #pcol + N' FROM
(
SELECT t1.ID, t1.b,t1.c, t2.linkdoc as data,t3.Name +N''URL'' as Type FROM t1 LEFT JOIN t2 ON t1.ID=t2.ID
LEFT JOIN t3 ON t2.iddoc=t3.iddoc
UNION
SELECT t1.ID, t1.b, t1.c, t2.info as data, t3.Name +N''Desc'' as Type FROM t1 LEFT JOIN t2 ON t1.ID=t2.ID
LEFT JOIN t3 ON t2.iddoc=t3.iddoc
)S
PIVOT
(
MAX(data)
FOR Type IN ('+#pcol +N')) AS piv'
EXEC sp_executesql #qu
Here's a sql fiddle for you :
http://sqlfiddle.com/#!6/9fb46/1
EDIT:explanation added
So basically I am using PIVOT, except that PIVOT can be done on a
single column , in our case, either on URL or Desc columns. But we
need both these columns to be pivoted,so I used UNION to get both into
a single column data like below
SELECT t1.ID, t1.b,t1.c, t2.linkdoc as data,t3.Name +N'URL' as Type FROM t1 LEFT JOIN t2 ON t1.ID=t2.ID
LEFT JOIN t3 ON t2.iddoc=t3.iddoc
UNION
SELECT t1.ID, t1.b, t1.c, t2.info as data, t3.Name +N'Desc' as Type FROM t1 LEFT JOIN t2 ON t1.ID=t2.ID
LEFT JOIN t3 ON t2.iddoc=t3.iddoc
which I then used in PIVOT like this :
Select ID,b,c,[ContractURL],[ContractDesc],[NotificationURL],[NotificationDesc]
FROM
(
SELECT t1.ID, t1.b,t1.c, t2.linkdoc as data,t3.Name +N'URL' as Type FROM t1 LEFT JOIN t2 ON t1.ID=t2.ID
LEFT JOIN t3 ON t2.iddoc=t3.iddoc
UNION
SELECT t1.ID, t1.b, t1.c, t2.info as data, t3.Name +N'Desc' as Type FROM t1 LEFT JOIN t2 ON t1.ID=t2.ID
LEFT JOIN t3 ON t2.iddoc=t3.iddoc
)S
PIVOT
(
MAX(data)
FOR Type IN ([ContractURL],[ContractDesc],[NotificationURL],[NotificationDesc])
)piv
Now for making this dynamic I calculated all the unique columns from table t3 like
SELECT Name+N'URL' AS type FROM t3 UNION SELECT Name+N'Desc' AS type FROM t3

Related

How to group total amount spend based on both ID and name

i have a table where
patientId | Units | Amount | PatientName
1234 | 1 | 20 |lisa
1111 | 5 | 10 |john
1234 | 10 | 200 |lisa
345 | 2 | 30 | xyz
i want to get ID in one column, then patient name then total amount spent by him on different items,
please note i have got patient name in the column above by doing a join on 2 tables using ID as the key
i am doing this to get this table
select t1.*,t2.name from table1 as t1 inner join table2 as t2
on t1.id = t2.id
then for adding i am trying to use the group by clause but that gives an error
please note i cannot use temp table in this, only need to do this using subquery, how to do it?
Are you looking for group by?
select t1.patientid, t2.patientname, sum(t1.amount)
from table1 t1 join
table2 t2
on t1.id = t2.id
group by t1.patientid, t2.patientname;
select t1.*,
t2.name
from table1 t1
inner join table2 t2
on t1.id = t2.id
group by t1.id, t2.name
What are table1 and table2 like? What's the error message?

Find values where related must have list of values

I'm trying to find a simple solution for my SQL Server problem.
I have two tables look like this:
table1
--id
-- data
table2
--id
--table1_id
--value
I have some records like this:
Table1
+-----------------------+
| id | data |
+-----------------------+
| 1 | ? |
+-----------------------+
| 2 | ? |
+-----------------------+
Table2
+-----------------------+
|id | table1_id | value |
+-----------------------+
| 1 | 1 | 'a' |
+-----------------------+
| 2 | 1 | 'b' |
+-----------------------+
| 3 | 2 | 'a' |
+-----------------------+
Now I want to get table1 with all it's additional values where the relation to table2 has 'a' AND 'b' as values.
So I would get the id 1 of table1.
Currently I have an query like this:
SELECT t1.[id], t1.[data]
FROM [table1] t1,
(SELECT [id]
FROM [table1] t1
JOIN [table2] t2 ON t1.[id] = t2.[table1_id] AND t2.[Value] IN('a', 'b')
GROUP BY t1[id]
HAVING COUNT(t2.[Value]) = 2) x
WHERE t1.id = x.id
Has anyone an idea on how to achieve my goal in a simpler way?
One way uses exists:
select t1.*
from table1 t1
where exists (select 1
from table2 t2
where t2.table1_id = t1.id and t2.value = 'a'
) and
exists (select 1
from table2 t2
where t2.table1_id = t1.id and t2.value = 'b'
);
This can take advantage of an index on table2(table1_id, value).
You could also write:
select t1.*
from table1 t1
where (select count(distinct t2.value)
from table2 t2
where t2.table1_id = t1.id and t2.value in ('a', 'b')
) = 2 ;
This would probably also have very good performance with the index, if table2 doesn't have duplicates.
SELECT T1.[id], T1.[data]
FROM table1 AS T1
JOIN table2 AS T2
ON T1.[id]=T2.[table1_id]
JOIN table2 AS T3
ON T1.[id]=T3.[table1_id]
WHERE
T2.[Value] ='a'
AND T3.[Value] = 'b'
As Gordon Linoff suggested, exists clause usage works as well and could be performance efficient depending on the data you are playing with.
you have to do several steps to solve the problem:
established which records are related to table 1 and table 2 and which of these are of value (A or B) and eliminate the repeated ones with the group by(InfoRelationate )
validate that only those related to a and b were allowed by means of a count in the table above (ValidateAYB)
see what data meets the condition of table1 and table 2 and joined table 1
this query meets the conditions
with InfoRelationate as
(
select Table2.table1_id,value
from Table2 inner join
Table1 on Table2.table1_id=Table1.id and Table2.value IN('a', 'b')
group by Table2.table1_id,value
),
ValidateAYB as
(
select InfoRelationate.table1_id
from InfoRelationate
group by InfoRelationate.table1_id
having count (1)=2
)
select InfoRelationate.table1_id,InfoRelationate.value
from InfoRelationate
inner join ValidateAYB on InfoRelationate.table1_id=ValidateAYB.table1_id
union all
select id,data
from Table1
Example code

Using the same table alias twice in a query

My coworker, who is new to ANSI join syntax, recently wrote a query like this:
SELECT count(*)
FROM table1 t1
JOIN table2 t2 ON
(t1.col_a = t2.col_a)
JOIN table3 t3 ON
(t2.col_b = t3.col_b)
JOIN table3 t3 ON
(t3.col_c = t1.col_c);
Note that table3 is joined to both table1 and table2 on different columns, but the two JOIN clauses use the same table alias for table3.
The query runs, but I'm unsure of it's validity. Is this a valid way of writing this query?
I thought the join should be like this:
SELECT count(*)
FROM table1 t1
JOIN table2 t2 ON
(t1.col_a = t2.col_a)
JOIN table3 t3 ON
(t2.col_b = t3.col_b AND
t3.col_c = t1.col_c);
Are the two versions functionally identical? I don't really have enough data in our database yet to be sure.
Thanks.
The first query is a join of 4 tables, the second one is a join of 3 tables. So I don't expect that both queries return the same numbers of rows.
SELECT *
FROM table1 t1
JOIN table2 t2 ON
(t1.col_a = t2.col_a)
JOIN table3 t3 ON
(t2.col_b = t3.col_b)
JOIN table3 t3 ON
(t3.col_c = t1.col_c);
The alias t3 is only used in the ON clause. The alias t3 refers to the table before the ON keyword. I found this out by experimenting. So the pervious query is equvivalent to
SELECT *
FROM table1 t1
JOIN table2 t2 ON
(t1.col_a = t2.col_a)
JOIN table3 t3 ON
(t2.col_b = t3.col_b)
JOIN table3 t4 ON
(t4.col_c = t1.col_c);
and this can be transfotmed in a traditional join
SELECT *
FROM table1 t1,
table2 t2,
table3 t3,
table3 t4
where (t1.col_a = t2.col_a)
and (t2.col_b = t3.col_b)
and (t4.col_c = t1.col_c);
The second query is
SELECT *
FROM table1 t1
JOIN table2 t2 ON
(t1.col_a = t2.col_a)
JOIN table3 t3 ON
(t2.col_b = t3.col_b AND
t3.col_c = t1.col_c);
This can also transformed in a traditional join
SELECT *
FROM table1 t1,
table2 t2,
table3 t3
where (t1.col_a = t2.col_a)
and (t2.col_b = t3.col_b)
AND (t3.col_c = t1.col_c);
These queries seem to be different. To proof their difference we use the following example:
create table table1(
col_a number,
col_c number
);
create table table2(
col_a number,
col_b number
);
create table table3(
col_b number,
col_c number
);
insert into table1(col_a, col_c) values(1,3);
insert into table1(col_a, col_c) values(4,3);
insert into table2(col_a, col_b) values(1,2);
insert into table2(col_a, col_b) values(4,2);
insert into table3(col_b, col_c) values(2,3);
insert into table3(col_b, col_c) values(2,5);
insert into table3(col_b, col_c) values(7,9);
commit;
We get the following output
SELECT *
FROM table1 t1
JOIN table2 t2 ON
(t1.col_a = t2.col_a)
JOIN table3 t3 ON
(t2.col_b = t3.col_b)
JOIN table3 t3 ON
(t3.col_c = t1.col_c)
| COL_A | COL_C | COL_A | COL_B | COL_B | COL_C | COL_B | COL_C |
|-------|-------|-------|-------|-------|-------|-------|-------|
| 1 | 3 | 1 | 2 | 2 | 3 | 2 | 3 |
| 4 | 3 | 4 | 2 | 2 | 3 | 2 | 3 |
| 1 | 3 | 1 | 2 | 2 | 5 | 2 | 3 |
| 4 | 3 | 4 | 2 | 2 | 5 | 2 | 3 |
SELECT *
FROM table1 t1
JOIN table2 t2 ON
(t1.col_a = t2.col_a)
JOIN table3 t3 ON
(t2.col_b = t3.col_b AND
t3.col_c = t1.col_c)
| COL_A | COL_C | COL_A | COL_B | COL_B | COL_C |
|-------|-------|-------|-------|-------|-------|
| 4 | 3 | 4 | 2 | 2 | 3 |
| 1 | 3 | 1 | 2 | 2 | 3 |
The number of rows retrieved is different and so count(*) is different.
The usage of the aliases was surprising. at least for me.
The following query works because t1 in the where_clause references table2.
select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_b<0;
The following query works because t1 in the where_clause references table1.
select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_c<0;
The following query raises an error because both table1 and table2 contain a column col_a.
select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_a<0;
The error thrown is
ORA-00918: column ambiguously defined
The following query works, the alias t1 refers to two different tables in the same where_clause.
select *
from table1 t1 join table2 t1 on(1=1)
where t1.col_b<0 and t1.col_c<0;
These and more examples can be found here: http://sqlfiddle.com/#!4/84feb/12
The smallest counter example
The smallest counter example is
table1
col_a col_c
1 2
table2
col_a col_b
1 3
table3
col_b col_c
3 5
6 2
Here the second query has an empty result set and the first query returns one row. It can be shown that the count(*) of the second query never exeeds the count(*)of the first query.
A more detailed explanation
This behaviour will became more clear if we analyze the following statement in detail.
SELECT t.col_b, t.col_c
FROM table1 t
JOIN table2 t ON
(t.col_b = t.col_c) ;
Here is the reduced syntax for this query in Backus–Naur form derived from the syntax descriptions in the SQL Language Reference of Oracle 12.2. Note that under each syntax diagram there is a link to the Backus–Naur form of this diagram, e.g Description of the illustration select.eps. "reduced" means that I left out all the possibilities that where not used, e,g. the select is defined as
select::=subquery [ for_update_clause ] ;
Our query does not use the optional for_update_clause, so I reduced the rule to
select::=subquery
The only exemption is the optional where-clause. I didn't remove it so that this reduced rules can be used to analyze the above query even if we add a where_clause.
These reduced rule will define only a subset of all possible select statements.
select::=subquery
subquery::=query_block
query_block::=SELECT select_list FROM join_clause [ where_clause ]
join_clause::=table_reference inner_cross_join_clause ...
table_reference::=query_table_expression t_alias query_table_expression::=table
inner_cross_join_clause::=JOIN table_reference ON condition
So our select statement is a query_block and the join_clause is of type
table_reference inner_cross_join_clause
where table_reference is table1 t and inner_cross_join_clause is JOIN table2 t ON (t.col_b = t.col_c). The ellipsis ... means that there could be additional inner_cross_join_clauses, but we do not need this here.
in the inner_cross_join_clause the alias t refers to table2. Only if these references cannot be satisfied the aliasmust be searched in an outer scope. So all the following expressions in the ONcondition are valid:
t.col_b = t.col_c
Here t.col_b is table2.col_b because t refers to the alias of its inner_cross_join_clause, t.col_c is table1.col_c. t of the inner_cross_join_clause (refering to table2) has no column col_c so the outer scope will be searched and an appropriate alias will be found.
If we have the clause
t.col_a = t.col_a
the alias can be found as alias defined in the inner_cross_join_clause to which this ON-condition belongs so t will be resolved to table2.
if the select list consists of
t.col_c, t.col_b, t.col_a
instead of * then the join_clause will be searched for an alias and t.col_c will be resolved to table1.col_c (table2 does not contain a column col_c), t.col_b will be resolved to table2.col_b (table1 does not contain a col_b) but t.col_a will raise the error
ORA-00918: column ambiguously defined
because for the select_list none of the aias definition has a precedenve over the other. If our query also has a where_clause then the aliases are resolved in the same way as if they are used in the select_list.
With more data, it will produce different results.
Your colleagues query is same as this.
select * from table3 where t3.col_b = 'XX'
union
select * from table3 where t3.col_c = 'YY'
or
select * from table3 where t3.col_b = 'XX' or t3.col_c = 'YY'
while your query is like this.
select * from table3 where t3.col_b ='XX' and t3.col_c='YY'
First one is like data where (xx or yy) while second one is data where ( xx and yy)

How to combine tables of different columns into 1 result set and make all attributes of same pk in 1 row with 1 SQL statement?

I got a problems in combining tables
I have 2 table:
t1:
id | name
----- ---------
1 | 'foo'
2 | 'bar'
t2:
id | type
------ ---------
1 | 'type1'
3 | 'type2'
I want to combine those tables into 1 result set and make all attributes of same primary key in 1 row. And with single SQL statement in Oracle. The primary key column with the same name (id in the sample) can't appear twice
The result should be:
id | name | type
----- --------- ---------
1 | 'foo' | 'type1'
2 | 'bar' | null
3 | null | 'type2'
Thanks in advance for any ideas and responses.
Update:
I tried Ani Menon's out join statement, but not 100% the expected result. The outer join gives null value if id exists in t1 but not in t2.
SELECT t1.id,t1.name,t2.type
FROM t1
FULL OUTER JOIN t2 ON t1.id=t2.id;
Returns
id | name | type
----- --------- ---------
1 | 'foo' | 'type1'
2 | 'bar' | null
null | null | 'type2'
Do a full outer join:
SELECT table1.id,table1.name,table2.type
FROM table1
FULL OUTER JOIN table2 ON table1.id=table2.id;
Edit:
Use coalesce(table1.id,table2.id) in place of table1.id in the query.
Similar to Ani's answer, but won't give you the null id:
select coalesce(table1.id, table2.id) as id, table1.name, table2.type
from table1 full outer join table2 on table1.id = table2.id;
might now be the optimal solution but this will work.
select allIDs.id, t1.name, t2.type
(select id from t1
union
select id from t2) allIDs left outer join t1 on allIDs.id = t1.id left outer join t2 on allIDs.id = t2.id
tested in sql server worked.
create table t1 (
id int,
name varchar(25)
)
create table t2
(
id int,
type varchar(25)
)
insert into t1
values(1, 'fool'),
(2,'bar')
insert into t2
values(1,'type1'),
(3,'type2')
select id, MAX(name) name, MAX(type) type from(
select id, name, null type from t1
union all
select id, null name, type from t2) combine group by id
SELECT t1.id,t1.name,t2.type
FROM t1
LEFT OUTER JOIN t2 ON t1.id=t2.id
union
select t2.id, t1.name, t2.type
from t2
left outer join t1 on t2.id = t1.id

Joining two sql tables with a one to many relationship, but want the max of the second table

I am trying to join two tables one is a unique feature the seconds is readings taken on several dates that relate to the unique features. I want all of the records in the first table plus the most recent reading. I was able to get the results I was looking for before adding the shape field. By using the code
SELECT
Table1.Name, Table1.ID, Table1.Shape,
Max(Table2.DATE) as Date
FROM
Table1
LEFT OUTER JOIN
Table2 ON Table1.ID = table2.ID
GROUP BY
Table1.Name, Table1.ID, Table1.Shape
The shape field is a geometry type and I get the error
'The type "Geometry" is not comparable. It can not be use in the Group By Clause'
So I need to go about it a different way, but not sure how.
Below is a sample of the two tables and the desired results.
Table1
Name| ID |Shape
AA1 | 1 | X
BA2 | 2 | Y
CA1 | 3 | Z
CA2 | 4 | Q
Table2
ID | Date
1 | 5/27/2013
1 | 6/27/2014
2 | 5/27/2013
2 | 6/27/2014
3 | 5/27/2013
3 | 6/27/2014
My Desired Result is
Name| ID |Shape |Date
AA1 | 1 | X | 6/27/2014
BA2 | 2 | Y | 6/27/2014
CA1 | 3 | Z | 6/27/2014
CA2 | 4 | Q | Null
You can do the aggregation on Table2 in a CTE, finding the MAX(DATE) for each ID, and then join that result to Table1:
WITH AggregatedTable2(ID, MaxDate) AS
(
SELECT
ID, MAX(DATE)
FROM
Table2
GROUP BY
ID
)
SELECT
t1.ID, t1.Name, t1.Shape, t2.MaxDate
FROM
Table1 t1
LEFT JOIN
AggregatedTable2 t2 ON t1.ID = t2.ID
Try casting geometry as a varchar.
Select Table1.Name, Table1.ID, cast(Table1.Shape as varchar(1)) AS Shape, Max(Table2.DATE) as Date
FROM Table1 LEFT OUTER JOIN
Table2 ON Table1.ID = table2.ID
Group By Table1.Name, Table1.ID, cast(Table1.Shape as varchar(1))
Try this:
SELECT t1.Name
, t1.ID
, t1.Shape
, MAX(t2.Date) As Date
FROM Table1 AS t1
LEFT JOIN Table2 AS t2
ON t2.ID = t1.ID
GROUP
BY t1.Name
, t1.ID
, t1.Shape