Join two or more tables that have no common fields - sql

I have 3 tables in a database
Project Material Used Document
------------ ------------------- ------------------------
PID | Name MID | PID | Name DocID | PID | Date
------------ ------------------- ------------------------
1 | A 1 | 1 | A1 1 | 1 | 1/1/2016
2 | B 2 | 1 | A1 2 | 1 | 1/2/2016
3 | C 3 | 1 | A1 3 | 2 | 1/3/2016
4 | 2 | A1 4 | 2 | 1/4/2016
5 | 2 | A1 5 | 2 | 1/5/2016
6 | 3 | A1 6 | 2 | 1/6/2016
7 | 3 | A1 7 | 2 | 1/7/2016
8 | 3 | A1 8 | 1 | 1/8/2016
9 | 3 | A1 9 | 1 | 1/9/2016
How to query with the results like below ?
------------------------------------------------------------------------
PID Project Name MID Material Name DocID Date
------------------------------------------------------------------------
1 A 1 A1 1 1/1/2016
1 A 2 A2 2 1/2/2016
1 A 3 A3 NULL NULL
2 B 4 B1 3 1/3/2016
2 B 5 B2 4 1/4/2016
2 B NULL NULL 5 1/5/2016
2 B NULL NULL 6 1/6/2016
2 B NULL NULL 7 1/7/2016
3 C 6 C1 8 1/8/2016
3 C 7 C2 9 1/9/2016
3 C 8 C3 NULL NULL
3 C 9 C4 NULL NULL
PID in Material and Document table is Foreign Key.
I am using Microsoft SQL 2008.
Is it possible ?

You seem to want lists in the columns. You can get this using full outer join and row_number():
select p.*, m.mid, m.name, d.docid, d.date
from project p left join
(select m.*, row_number() over (partition by pid order by mid) as seqnum
from materials
) m
on p.pid = m.pid full outer join
(select d.*, row_number() over (partition by pid order by docid) as seqnum
from documents
) d
on p.pid = d.pid and m.seqnum = d.seqnum;
Hmmm, try this version:
select p.*, md.mid, md.name, md.docid, md.date
from project p left join
(select m.id, m.name, d.docid, d.date
from (select m.*, row_number() over (partition by pid order by mid) as seqnum
from materials
) m full outer join
(select d.*, row_number() over (partition by pid order by docid) as seqnum
from documents
) d
on p.pid = d.pid and m.seqnum = d.seqnum
) md
on p.pid = md.pid;

Related

Create column based on multiple possible tables using SQL

We want to add the column doc_val to the Main table such that the value will be the val of the table determined by the doc_type at the row determined by doc_id.
For example, the first value of val will be 57 because in the first row of Main the doc_type is 'doc_a' and the doc_id is 1 and the val of id 1 in table A is 57.
Table Main
id | doc_type | doc_id
----------------------
1 | 'doc_a' | 1
2 | 'doc_a' | 2
3 | 'doc_b' | 3
4 | 'doc_c' | 1
5 | 'doc_c' | 3
Table A Table B Table C
id | val id | val id | val
-------- -------- --------
1 | 57 1 | 33 1 | 55
2 | 63 2 | 26 2 | 22
3 | 72 3 | 19 3 | 54
The table we want is below.
id | doc_type | doc_id | doc_val
--------------------------------
1 | 'doc_a' | 1 | 57
2 | 'doc_a' | 2 | 63
3 | 'doc_b' | 3 | 19
4 | 'doc_c' | 1 | 55
5 | 'doc_c' | 3 | 54
How can we do this with an SQL query?
You can use left join:
select m.*, coalesce(a.val, b.val, c.val) as val
from main m left join
a
on m.doc_id = a.id and m.doc_type = 'doc_a' left join
b
on m.doc_id = b.id and m.doc_type = 'doc_b' left join
c
on m.doc_id = c.id and m.doc_type = 'doc_c';
An alternative method (which is probably less performant) uses union all:
select m.*, abc.val
from main left join
((select id, 'doc_a' as doc_type, val from a
) union all
(select id, 'doc_b' as doc_type, val from b
) union all
(select id, 'doc_c' as doc_type, val from c
)
) abc
on m.id = abc.id and m.doc_type = abc.doc_type;

For every value in row c in d, return rows with maximum value of a

I have 4 columns a ,b ,c, d
sample data
a | b | c | d |
1 | 1 | 101 | 0
2 | 1 | 101 | 0
3 | 1 | 101 | 1
4 | 1 | 102 | 0
5 | 1 | 102 | 0
1 | 2 | 101 | 0
2 | 2 | 101 | 1
Write a SQL command such that it should return those rows where for every value of c in b, return rows with maximum a
i.e
Expect output
a | b | c | d |
3 | 1 | 101 | 1
5 | 1 | 102 | 0
2 | 2 | 101 | 1
You can use a correlated subquery:
select t.*
from t
where t.a = (select max(t2.a) from t t2 where t2.b = t.b and t2.c = t.c);
With an index on t(b, c, a), this often has the best performance.
An alternative is window functions:
select t.*
from (select t.*, row_number() over (partition by b, c order by a desc) as seqnum
from t
) t
where seqnum = 1;
You don't mention the database you are using. In PostgreSQL you can do:
select distinct on (b, c) a, b, c, d
from t
order by b, c, a desc

How to do it by simple sql or procedure

object_tbl:
objId(primary key) | name
1 | A
2 | B
3 | C
document_tbl:
documentId | sourceId
1 | 2
2 | 2
3 | 1
4 | 3
5 | 3
6 | 3
objToDoc_tbl:
id | objectId | documentId
1 | 1 | 2
2 | 2 | 4
3 | 2 | 6
4 | 1 | 5
5 | 3 | 1
6 | 1 | 2
Inner join of all table
A 2 2
B 4 3
B 6 3
A 5 3
C 1 2
A 2 2
A 2
B 1
C 1
so Answer is 1(As A is only 2)
Question: - How many object are related to documents from multiple sources.
How we can write sql query for this or it can only we solved by procedure
You can join all the tables and then use having to get objects with count > 1.
with t as (
select t.name, count(distinct d.sourceid) as count
from objtodoc_tbl o
join document_tbl d on o.documentid = d.documentid
join object_tbl t on o.objectid = t.name
group by t.name
having count(distinct d.sourceid) > 1
)
select count(t.name) from t;

Remove duplicates from inner join with one table

I have following table
id | cId
---------------
1 | 1
2 | 1
3 | 2
4 | 2
I need help with query, that return following result
id1 | id2 | cId
----------------------
1 | 2 | 1
3 | 4 | 2
SELECT s1.id firstC, s2.id secondC
FROM SingleTable s1 inner join SingleTable s2 on s1.cId = s2.cId
But i get duplicates. I want gte result without duplicates.
My result:
id1 | id2 | cId
---------------
1 | 1 | 1
2 | 1 | 1
1 | 2 | 1
2 | 2 | 1
3 | 3 | 2
4 | 3 | 2
3 | 4 | 2
4 | 4 | 2
Please help.
You can just group by the cId and then select the highest and lowest id for every cId
SELECT cId,
min(id) as id1,
max(id) as id2
FROM SingleTable
GROUP BY cId
Remove the duplicates (s1.id = s2.id) with an extra condition (s1.id < s2.id):
SELECT s1.id firstC, s2.id secondC
FROM SingleTable s1 INNER JOIN SingleTable s2
ON (s1.cId = s2.cId AND s1.id < s2.id)

Left Join on Associative Table

I have three tables
Prospect -- holds prospect information
id
name
projectID
Sample data for Prospect
id | name | projectID
1 | p1 | 1
2 | p2 | 1
3 | p3 | 1
4 | p4 | 2
5 | p5 | 2
6 | p6 | 2
Conjoint -- holds conjoint information
id
title
projectID
Sample data
id | title | projectID
1 | color | 1
2 | size | 1
3 | qual | 1
4 | color | 2
5 | price | 2
6 | weight | 2
There is an associative table that holds the conjoint values for the prospects:
ConjointProspect
id
prospectID
conjointID
value
Sample Data
id | prospectID | conjointID | value
1 | 1 | 1 | 20
2 | 1 | 2 | 30
3 | 1 | 3 | 50
4 | 2 | 1 | 10
5 | 2 | 3 | 40
There are one or more prospects and one or more conjoints in their respective tables. A prospect may or may not have a value for each conjoint.
I'd like to have an SQL statement that will extract all conjoint values for each prospect of a given project, displaying NULL where there is no value for a value that is not present in the ConjointProspect table for a given conjoint and prospect.
Something along the lines of this for projectID = 1
prospectID | conjoint ID | value
1 | 1 | 20
1 | 2 | 30
1 | 3 | 50
2 | 1 | 10
2 | 2 | NULL
2 | 3 | 40
3 | 1 | NULL
3 | 2 | NULL
3 | 3 | NULL
I've tried using an inner join on the prospect and conjoint tables and then a left join on the ConjointProspect, but somewhere I'm getting a cartesian products for prospect/conjoint pairs that don't make any sense (to me)
SELECT p.id, p.name, c.id, c.title, cp.value
FROM prospect p
INNER JOIN conjoint c ON p.projectID = c.projectid
LEFT JOIN conjointProspect cp ON cp.prospectID = p.id
WHERE p.projectID = 2
ORDER BY p.id, c.id
prospectID | conjoint ID | value
1 | 1 | 20
1 | 2 | 30
1 | 3 | 50
1 | 1 | 20
1 | 2 | 30
1 | 3 | 50
1 | 1 | 20
1 | 2 | 30
1 | 3 | 50
2 | 1 | 10
2 | 2 | 40
2 | 1 | 10
2 | 2 | 40
2 | 1 | 10
2 | 2 | 40
3 | 1 | NULL
3 | 2 | NULL
3 | 3 | NULL
Guidance is very much appreciated!!
Then this will work for you... Prejoin a Cartesian against all prospects and elements within that project via a select as your first FROM table. Then, left join to the conjoinprospect. You can obviously change / eliminate certain columns from result, but at least all is there, in the join you want with exact results you are expecting...
SELECT
PJ.*,
CJP.Value
FROM
( SELECT
P.ID ProspectID,
P.Name,
P.ProjectID,
CJ.Title,
CJ.ID ConJointID
FROM
Prospect P,
ConJoint CJ
where
P.ProjectID = 1
AND P.ProjectID = CJ.ProjectID
ORDER BY
1, 4
) PJ
LEFT JOIN conjointProspect cjp
ON PJ.ProspectID = cjp.prospectID
AND PJ.ConjointID = cjp.conjointid
ORDER BY
PJ.ProspectID,
PJ.ConJointID
Your cartesian product is a result of joining by project Id - in your sample data there are 3 prospects with a project id of 1 and 3 conjoints with a project id of 1. Joining based on project id should then result in 9 rows of data, which is what you're getting. It looks like you really need to join via the conjointprospects table as that it what holds the mapping between prospects and conjoint.
What if you try something like:
SELECT p.id, p.name, c.id, c.title, cp.value
FROM prospect p
LEFT JOIN conjointProspect cp ON cp.prospectID = p.id
RIGHT JOIN conjoint c ON cp.conjointID = c.id
WHERE p.projectID = 2
ORDER BY p.id, c.id
Not sure if that will work, but it seems like conjointprospects needs to be at the center of your join in order to correctly map prospects to conjoints.