Where condition based on link table - sql

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

Related

Find missing setting

I have two tables in my DB. Table A is informational data table and Table B is a setting table. How do I find Table A is missing one of the setting in Table B.
E.G.
Table A
username setting
Mark 1
Mark 2
Martin 2
Jane 1
Table B
Possible_Setting
1
2
3
Result Table
username missing_setting
Mark 3
Martin 1
Martin 3
Jane 2
Jane 3
Thanks for help!
This may be inefficient if table sizes are significant, owing to the cross join but its the only answer I could come up with.
SELECT a.username, b.Possible_Setting AS missing_setting
FROM
(SELECT DISTINCT username FROM TableA a) a
CROSS JOIN TableB b
WHERE
NOT EXISTS (
SELECT *
FROM TableA real_a
WHERE real_a.username = a.username
AND real_a.setting = b.Possible_Setting)
ORDER BY 1, 2
Setup code:
CREATE TABLE TableA (username varchar(20), setting tinyint)
CREATE TABLE TableB (Possible_Setting tinyint PRIMARY KEY)
INSERT TableA VALUES
('Mark', 1),
('Mark', 2),
('Martin', 2),
('Jane', 1)
INSERT TableB VALUES
(1),
(2),
(3)

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 get data from multiple tables using SQL query

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

ms-access 2010: count duplicate names per household address

I am currently working with a spreadsheet in MS Access 2010 which contains about 130k rows of information about people who voted in a local election recently. Each row has their residential information (street name, number, postcode etc.) and personal information (title, surname, forename, middle name, DOB etc.). Each row represents an individual person rather than a household (therefore in many cases the same residential address appears more than once as more than one person resides in a particular household).
What I want to achieve is basically to create a new field in this dataset called 'count'. I want this field to give me a count of how many different surnames reside at a single address.
Is there an SQL script that will allow me to do this in Access 2010?
+------------------+----------+-------+---------+----------+-------------+
| PROPERTYADDRESS1 | POSTCODE | TITLE | SURNAME | FORENAME | MIDDLE_NAME |
+------------------+----------+-------+---------+----------+-------------+
FAKEADDRESS1 EEE 5GG MR BLOGGS JOE N
FAKEADDRESS2 EEE 5BB MRS BLOGGS SUZANNE P
FAKEADDRESS3 EEE 5RG MS SMITH PAULINE S
FAKEADDRESS4 EEE 4BV DR JONES ANNE D
FAKEADDRESS5 EEE 3AS MR TAYLOR STUART A
The following syntax has got me close so far:
SELECT COUNT(electoral.SURNAME)
FROM electoral
GROUP BY electoral.UPRN
However, instead of returning me all 130k odd rows, it only returns me around 67k rows. Is there anything I can do to the syntax to achieve the same result, but just returning every single row?
Any help is greatly appreciated!
Thanks
You could use something like this:
select *,
count(surname) over (partition by householdName)
from myTable
If you have only one column which contains the name,
ex: Rob Adams
then you can do this to have all the surnames in a different column so it will be easier in the select:
SELECT LEFT('HELLO WORLD',CHARINDEX(' ','HELLO WORLD')-1)
in our example:
select right (surmane, charindex (' ',surname)-1) as surname
example on how to use charindex, left and right here:
http://social.technet.microsoft.com/wiki/contents/articles/17948.t-sql-right-left-substring-and-charindex-functions.aspx
if there are any questions, leave a comment.
EDIT: I edited the query, had a syntax error, please try it again. This works on sql server.
here is an example:
create table #temp (id int, PropertyAddress varchar(50), surname varchar(50), forname varchar(50))
insert into #temp values
(1, 'hiddenBase', 'Adamns' , 'Kara' ),
(2, 'hiddenBase', 'Adamns' , 'Anne' ),
(3, 'hiddenBase', 'Adamns' , 'John' ),
(4, 'QueensResidence', 'Queen' , 'Oliver' ),
(5, 'QueensResidence', 'Queen' , 'Moira' ),
(6, 'superSecretBase', 'Diggle' , 'John' ),
(7, 'NandaParbat', 'Merlin' , 'Malcom' )
select * from #temp
select *,
count (surname) over (partition by PropertyAddress) as CountMembers
from #temp
gives:
1 hiddenBase Adamns Kara 3
2 hiddenBase Adamns Anne 3
3 hiddenBase Adamns John 3
7 NandaParbat Merlin Malcom 1
4 QueensResidence Queen Oliver 2
5 QueensResidence Queen Moira 2
6 superSecretBase Diggle John 1
Your query should look like this:
select *,
count (SURNAME) over (partition by PropertyAddress) as CountFamilyMembers
from electoral
EDIT
If over partition by isn't supported, then I guess you can get to your desired result by using group by
select *,
count (SURNAME) over (partition by PropertyAddress) as CountFamilyMembers
from electoral
group by -- put here the fields in the select (one by one), however you can't write group by *
GROUP BY creates an aggregate query, so it's by design that you get fewer records (one per UPRN).
To get the count for each row in the original table, you can join the table with the aggregate query:
SELECT electoral.*, elCount.NumberOfPeople
FROM electoral
INNER JOIN
(
SELECT UPRN, COUNT(*) AS NumberOfPeople
FROM electoral
GROUP BY UPRN
) AS elCount
ON electoral.UPRN = elCount.UPRN
Given the update I want to post another answer. Try it like this:
create table #temp2 ( PropertyAddress1 varchar(50), POSTCODE varchar(20), TITLE varchar (20),
surname varchar(50), FORENAME varchar(50), MIDDLE_NAME varchar (50) )
insert into #temp2 values
('FAKEADDRESS1', 'EEE 5GG', 'MR', 'BLOGGS', 'JOE', 'N'),
('FAKEADDRESS1', 'EEE 5BB', 'MRS', 'BLOGGS', 'SUZANNE', 'P'),
('FAKEADDRESS2', 'EEE 5RG', 'MS', 'SMITH', 'PAULINE', 'S'),
('FAKEADDRESS3', 'EEE 4BV', 'DR', 'JONES', 'ANNE', 'D'),
('FAKEADDRESS4', 'EEE 3AS', 'MR', 'TAYLOR', 'STUART', 'A')
select PropertyAddress1, surname,count (#temp2.surname) as CountADD
into #countTemp
from #temp2
group by PropertyAddress1, surname
select * from #temp2 t2
left join #countTemp ct
on t2.PropertyAddress1 = ct.PropertyAddress1 and t2.surname = ct.surname
This yields:
PropertyAddress1 POSTCODE TITLE surname FORENAME MIDDLE_NAME PropertyAddress1 surname CountADD
FAKEADDRESS1 EEE 5GG MR BLOGGS JOE N FAKEADDRESS1 BLOGGS 2
FAKEADDRESS1 EEE 5BB MRS BLOGGS SUZANNE P FAKEADDRESS1 BLOGGS 2
FAKEADDRESS2 EEE 5RG MS SMITH PAULINE S FAKEADDRESS2 SMITH 1
FAKEADDRESS3 EEE 4BV DR JONES ANNE D FAKEADDRESS3 JONES 1
FAKEADDRESS4 EEE 3AS MR TAYLOR STUART A FAKEADDRESS4 TAYLOR 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