How to get data from multiple tables using SQL query - sql

I have three Tables Administration with relation one-to-many with Telephone, Fax Tables:
Administration : Id_Administration, Lib_Administration
Telephone: Id_Phone, Phone_Number, Id_Administration
Fax: Id_Fax, Fax_Number, Id_Administration
Administration table contains:
Id_Administration Lib_Administration
1 adminstration1
2 adminstration2
Telephone table contains:
Id_Phone Phone_Number Id_Administration
1 0313131 1
2 0212121 1
3 0353535 2
4 0343434 2
Fax table contains:
Id_Fax Fax_Number Id_Administration
1 0323232 1
2 0363636 2
3 0373737 2
I want to make a query to show this result:
Id_Administration Lib_Administration Phone_Number Fax_Number
1 adminstration1 0313131 0323232
0212121
2 adminstration2 0353535 0363636
0343434 0373737
I used this query
SELECT Administration.Id_Administration, Administration.Lib_Administration,Telephone.Phone_Number, Fax.Fax_Number
FROM ((Administration INNER JOIN
Telephone ON Administration.Id_Administration = Telephone.Id_Administration) INNER JOIN
Fax ON Administration.Id_Administration = Fax.Id_Administration)
But the result was iterated like this:
Id_Administration Lib_Administration Phone_Number Fax_Number
1 adminstration1 0313131 0323232
0212121 0323232
2 adminstration2 0353535 0363636
0343434 0363636
0353535 0373737
0343434 0373737
I used left join but i didn't get the right result, so where is the problem in my query?

Everything is possible, but sometimes hard work to achieve our goal is not worth it. Are you trying to prepare a recordset for your form?
In this scenario I would have 2 subforms, for phone and fax numbers. No need to load all in one recordset.

I don't think the original table structure works. In the posted DDL you have administration-level granularity. What you want to do is pair a phone number with a fax number- I would call this the office-level granularity. What you can do is add another level of granularity by creating an Office table. In the inserts, link each phone and fax number also to this office ID. For the Select, join the phone and fax tables also to this Id_Office. This should pair the phones by both Organization and Office to give the expected output.
For example if you include an Office table similar to below:
CREATE TABLE Administration (Id_Administration int, Lib_Administration varchar(100))
INSERT INTO Administration (Id_Administration, Lib_Administration) VALUES
(1, 'adminstration1'), (2, 'adminstration2')
CREATE TABLE Office(Id_Administration int, Id_Office int, Lib_Office varchar(100))
INSERT INTO Office (Id_Administration, Id_Office, Lib_Office) VALUES
(1, 1, 'A1 Office 1'), (1, 2, 'A1 Office 2')
,(2, 1, 'A2 Office 2'), (2, 2, 'A2 Office 2')
CREATE TABLE Fax (Id_Fax int, Fax_Number char(7), Id_Administration int, Id_Office int)
INSERT INTO Fax (Id_Fax, Fax_Number, Id_Administration, Id_Office) VALUES
(1, '0323232', 1, 1), (2, '0363636', 2, 1)
,(3, '0373737', 2, 2)
CREATE TABLE Telephone (Id_Phone int, Phone_Number char(7), Id_Administration int, Id_Office int)
INSERT INTO Telephone (Id_Phone, Phone_Number, Id_Administration, Id_Office) VALUES
(1, '0313131', 1, 1)
,(2, '0212121', 1, 2)
,(3, '0353535', 2, 1)
,(4, '0343434', 2, 2)
SELECT A.Id_Administration, A.Lib_Administration, T.Phone_Number, COALESCE(F.Fax_Number, '') AS Fax_Number
FROM Administration A LEFT JOIN Office O ON A.Id_Administration = O.Id_Administration
LEFT JOIN Fax F ON F.Id_Administration = A.Id_Administration AND F.Id_Office = O.Id_Office
LEFT JOIN Telephone T ON T.Id_Administration = A.Id_Administration AND T.Id_Office = O.Id_Office
This produces output:
Id_Administration Lib_Administration Phone_Number Fax_Number
1 adminstration1 0313131 0323232
1 adminstration1 0212121
2 adminstration2 0353535 0363636
2 adminstration2 0343434 0373737
http://sqlfiddle.com/#!18/27964/3/0

Related

Convert from an EAV table in SQL

Im having issues converting to format of an EAV table to something useful. The link table is confusing me and I dont really know how to start fixing this. Anyone have suggestions?
Contacts table
con_id Name Data
1 email a#gmail.com
2 phone 123
3 email b#gmail.com
4 phone 456
Link table (maps actual user accounts to rows in the Contacts table):
acct_id con_id
1 1
1 2
2 3
2 4
END GOAL:
acct_id Email Phone
1 a#gmail.com 123
2 b#gmail.com 456
http://sqlfiddle.com/#!4/7cf20/5/0
CREATE TABLE Contacts
(con_id int, Name varchar2(5), Data varchar2(11))
;
INSERT ALL
INTO Contacts (con_id, Name, Data)
VALUES (1, 'email', 'a#gmail.com')
INTO Contacts (con_id, Name, Data)
VALUES (2, 'phone', '123')
INTO Contacts (con_id, Name, Data)
VALUES (3, 'email', 'b#gmail.com')
INTO Contacts (con_id, Name, Data)
VALUES (4, 'phone', '456')
SELECT * FROM dual
;
CREATE TABLE Link
(acct_id int, con_id int)
;
INSERT ALL
INTO Link (acct_id, con_id)
VALUES (1, 1)
INTO Link (acct_id, con_id)
VALUES (1, 2)
INTO Link (acct_id, con_id)
VALUES (2, 3)
INTO Link (acct_id, con_id)
VALUES (2, 4)
SELECT * FROM dual
;
Query -
select * from (
select acct_id, name, Data
from contacts c, Link l
where c.con_id = l.con_id
)
pivot (max(Data) for name in ('email' as Email,'phone' as Phone));
Output -
ACCT_ID EMAIL PHONE
1 a#gmail.com 123
2 b#gmail.com 456

How to create view with two counts having different where clause from same table

I have two tables
Schools
CREATE TABLE Schools
(
BlockID INT,
SchoolID VARCHAR,
SchoolName VARCHAR,
powerAvail BIT,
waterAvail BIT
)
Blocks
CREATE TABLE Blocks
(
BlockID INT,
BlockName VARCHAR
)
Inserting values:
Insert into Schools (1, 1, 'a', 0, 1)
Insert into Schools (2, 2, 'b', 1, 1)
Insert into Schools (2, 3, 'c', 0, 1)
Insert into Blocks (1, 'A')
Insert into Blocks (2, 'B')
Now I have to create a view to list 3 columns: Block Name, Number of schools in a block with power available 1, Number of schools in a block with water available 1
grouping result by block name.
Note: the power available condition should not be dependent on water available.
You should be able to do:
SELECT B.BlockName,
SUM(CAST(powerAvail as int)) as num_powerAvail,
SUM(CAST(waterAvail as int)) as num_waterAvail
FROM Schools S JOIN
Blocks B
ON S.BlockID = B.BlockID
GROUP BY B.BlockName;
You can join tables School and Blocks on BlockID then do a sum when power or water available is 1.
SELECT
B.BlockName,
SUM(case when powerAvail=1 then 1 else 0 end) as 'Count_Schools_Power_Available',
SUM(case when waterAvail=1 then 1 else 0 end) as 'Count_Schools_Water_Available'
FROM
Schools S,
Blocks B
WHERE
S.BlockID = B.BlockID
GROUP BY
B.BlockName;
Result:
BlockName Count_Schools_Power_Available Count_Schools_Water_Available
A 0 1
B 1 2

Where condition based on link table

I have a table of Users. Each User can be in multiple Disciplines, and they are linked by a link table, User_Discipline. The tables are pretty straight forward:
User
ID Name more...
3 | John Doe | ...
7 | Jane Smith | ...
12 | Joe Jones | ...
Discipline
ID name
1 | Civil
2 | Mechanical
3 | Piping
User_Discipline
UserID DisciplineID
3 | 2
3 | 1
7 | 2
12 | 3
Say John Doe is the logged in user. He needs to be able to select a list of all of the users in any of his disciplines. In the given example, I need a query that would return a list with John and Jane, since John is both Civil and Mechanical, and Jane is in Mechanical.
I think sub-selects are involved, but all the reading I've done so far have shown how to do subselects checking for one value (say, John's Civil Discipline). I need to be able to perform a query that runs a WHERE condition but matches any of John's Disciplines many-to-many with others' Disciplines.
I'm using the DataTables Editor .NET library to do the SQL, but I can translate an answer in regular SQL markup to that library. The only limitation of the library that I might encounter here is that everything would have to be done in one SQL statement. I appreciate any help!
Something like this?
SELECT DISTINCT [User].ID, [User].Name
FROM [User]
JOIN User_Discipline
ON [User].ID = User_Discipline.UserID
WHERE
User_Discipline.DisciplineID IN (
SELECT DisciplineID
FROM User_Discipline
WHERE UserID = <<John Doe's userID>>
)
You can do it all with inner joins:
declare #users table (id int, fullname varchar(50))
declare #disciplines table (id int, discname varchar(50))
declare #userdisciplines table (userid int, discid int)
insert into #users VALUES (3, 'John Doe')
insert into #users VALUES (7, 'Jane Smith')
insert into #users VALUES (12, 'Joe Jones')
insert into #disciplines VALUES (1, 'Civil')
insert into #disciplines VALUES (2, 'Mechanical')
insert into #disciplines VALUES (2, 'Piping')
insert into #userdisciplines VALUES (3, 2)
insert into #userdisciplines VALUES (3, 1)
insert into #userdisciplines VALUES (7, 2)
insert into #userdisciplines VALUES (12, 3)
SELECT distinct id, fullname from #users u
INNER JOIN #userdisciplines ud ON ud.userid = u.id
INNER JOIN
(SELECT ud.discid FROM #users u
inner join #userdisciplines ud on ud.userid = u.id
WHERE u.fullname = 'John Doe') d ON d.discid = ud.discid

Querying SQL Server table with different values in same column with same ID [duplicate]

This question already has answers here:
Querying SQL table with different values in same column with same ID
(2 answers)
Closed 6 years ago.
I have an SQL Server 2012 table with ID, First Name and Last name. The ID is unique per person but due to an error in the historical feed, different people were assigned the same id.
------------------------------
ID FirstName LastName
------------------------------
1 ABC M
1 ABC M
1 ABC M
1 ABC N
2 BCD S
3 CDE T
4 DEF T
4 DEF T
There are two ID's which are present multiple time. 1 and 4. The rows with id 4 are identical. I dont want this in my result. The rows with ID 1, although the first name is same, the last name is different for 1 row. I want only those ID's whose ID is same but one of the first or last names is different.
I tried loading ID's which have multiple occurrences into a temp table and tried to compare it against the parent table albeit unsuccessfully. Any other ideas that I can try and implement?
This is the output I am looking for
ID
---
1
If you want the ids, then use aggregation and having:
select id
from t
group by id
having min(firstname) <> max(firstname) or min(lastname) <> max(lastname);
Try This:
CREATE TABLE #myTable(id INT, firstname VARCHAR(50), lastname VARCHAR(50))
INSERT INTO #myTable VALUES
(1, 'ABC', 'M'),
(1, 'ABC', 'M'),
(1, 'ABC', 'M'),
(1, 'ABC', 'N'),
(2, 'BCD', 'S'),
(3, 'CDE', 'T'),
(4, 'DEF', 'T'),
(4, 'DEF', 'T')
SELECT id FROM (
SELECT DISTINCT id, firstname, lastname
FROM #myTable) t GROUP BY id HAVING COUNT(*)>1
OUTPUT is : 1

How can I change my INNER JOIN to an OUTER JOIN?

I have 2 tables : Users and Documents.
1 User can have 0 or several documents.
I would like to display each user and their documents, but the problem : I would like to display users that have no documents, in a result like this (assume IdUsers 3 and 5 have no documents):
IdUser IdDocument DocumentName
====== ========== ============
1 1 test11.pdf
1 2 test12.pdf
1 3 test13.pdf
2 4 test21.pdf
2 5 test21.pdf
3 NULL NULL
4 6 test41.pdf
5 NULL NULL
You will use a LEFT JOIN to perform this operation.
create table users
(
userid int
)
create table documents
(
documentid int,
userid int,
documentname varchar(10)
)
insert into users values (1)
insert into users values (2)
insert into users values (3)
insert into users values (4)
insert into documents values (1, 1, 'test')
insert into documents values (2, 1, 'test 1')
insert into documents values (3, 2, 'test 2')
insert into documents values (3, 3, 'test 3')
select *
from users u
left join documents d
on u.userid = d.userid
see a sqlfiddle for a test
You should do some research on JOINs, here is a good description of the JOINs:
A Visual Explanation of SQL Joins