Link one table to another through two columns in SQL - sql

So I have two Tables. The first are Names and their qualifications:
Users:
Name Qualification
---------------------
User1 QualA
User1 QualB
User1 QualC
User2 QualA
User2 QualD
Then a Second table that links two qualifications from the first to another attribute:
Attributes:
Attribute Qual1 Qual2
------------------------
Attr1 QualA QualC
Attr2 QualB QualC
Attr3 QualA QualD
Attr4 QualB QualD
Now I want to query the data so I get something like this in return:
User Attribute
------------------
User1 Attr1
User1 Attr2
User2 Attr3
So if the Name has the two qualifications required for the Attribute, they can be associated together.

I would use this:
select
name, attribute
from
users inner join attributes
on users.qualification in (attributes.qual1, attributes.qual2)
group by attribute, name
having count(*)=2
I am trying to join each qualification for each user with attributes tables, based on any of the qualifications needed for the attribute. Then I'm grouping by attribute and name and counting the rows.
If a combination of username and attribute has 2 rows, it means that the user has two qualifications for the attribute, and we have to show it.

I had to produce a very similar query, however in my scenario Individuals or Users could be issued with the same Qualification more than once. Hence I had to come with something a bit more complicated than fthiella's solution.
The query I came up with is as follows:
SELECT
j2.name,
Qualification1,
Qualification2,
t3.Attribute
FROM
(SELECT
t1.name,
t1.qualification AS Qualification1,
J1.qualification AS Qualification2,
Rank() over (Partition BY t1.name, t1.qualification ORDER BY t1.qualification, J1.qualification) AS rank1,
Rank() over (Partition BY t1.name, J1.qualification ORDER BY J1.qualification, t1.qualification) AS rank2
FROM
Users t1
LEFT JOIN
(SELECT
t2.name,
t2.qualification
FROM
Users t2) J1
ON
t1.name = J1.name) J2
LEFT JOIN
Attributes t3
ON
t3.Qual1 = Qualification1
AND t3.Qual2 = Qualification2
WHERE
rank2 <= rank1
AND t3.Attribute IS NOT NULL
Basically I'm joining the table Users upon itself, listing all qualification combinations for each user without duplications. This then allows us to do a simple join of the Attributes table.
Of course if your not working with Sql server 2005 or later than the Rank function won't be available to you.

Related

Select from 1 table where conditions are in another table?

Here is my table in my database:
table1 table2
---------|-------- --------|
UserID |Type UserID |
---------|-------- --------|
user1 |Busy user1 |
user2 |Free user2 |
user3 |Free user3 |
user4 |Busy user4 |
I would like to select all the userID from table1 where the Type=Free. The only correlation from one table to another is that they have the same UserID in both tables.
What I have so far is :
SELECT UserID
FROM Data.Users
INNER JOIN Data.UserType
ON Data.Users.UserID=Data.UserType.UserID
This only gives me back ALL the userID where the UserID from table 1 = UserID from table 2.
I would like to be able to make it also check for the user type. Something like
SELECT Username FROM Data.Users WHERE TABLE2.UserID = TABLE1.UserID AND
TABLE2.UserType = "free"
I'm new to SQL so I don't think I'm Googling the right thing as well
So your query is working you just need to filter it:
SELECT Data.Users.Name
FROM Data.Users
INNER JOIN Data.UserType
ON Data.Users.UserID=Data.UserType.UserID
WHERE Data.UserType.Type = 'Free'
You have to ensure you are first selecting the data that you want, in this case "Type". So that's all we need to be concerned with when selecting information. However, for illustration purposes (and general good etiquette) lets also select the userID, but we'll take this from table1.
So we want to select Type from Table 2, and UserID from table 1, but where UserID is the same on both tables.
To do what we need to do the following:
SELECT two.Type, one.UserID FROM table2 two INNER JOIN table1 one ON two.UserID = one.UserID
This selects the information, from table2 and we assign it the reference two. Then we join table1 and assign the reference one. We can then grab the requested information in the select using the references we assigned, whilst also ensuring the data is joined where the two tables have identical values.
The above example will select all values where the userID's match, and is used for illustration purposes. Then, to narrow it down, all you have to do is specify the WHERE value on top of this. So:
SELECT two.Type, one.UserID FROM table2 two INNER JOIN table1 one ON two.UserID = one.UserID WHERE two.Type = "Free"

SQL query (Join without duplicates)

I have tables users and topics. Every user can have from 0 to several topics (one-to-many relationship).
How I can get only those users which have at least one topic?
I need all columns from users (without columns from topics) and without duplicates in table users. In last column I need number of topics.
UPDATED:
Should be like this:
SELECT user.*, count(topic.id)
FROM ad
LEFT JOIN topic ON user.id = topic.ad
GROUP BY user.id
HAVING count(topic.id) > 0;
but it takes 0 result. But it should not be 0.
Firstly you need to have your two tables, because you have left limited information about your table structure I will use an example to explain how this works, you should then be able to easily apply this to your own tables.
Firstly you need to have two tables (which you do)
Table "user"
id | name
1 | Joe Bloggs
2 | Eddy Ready
Table "topic"
topicid | userid | topic
1 | 1 | Breakfast
2 | 1 | Lunch
3 | 1 | Dinner
Now asking for a count against each user is done using the follwing;
SELECT user.name, count(topic.topicid)
FROM user
INNER JOIN topic ON user.id = topic.userid
GROUP BY user.name
If you use a left join, this will include records from the "user" table which does not have any rows in the "topic" table, however if you use an INNER JOIN this will ONLY include users who have a matching value in both tables.
I.e. because the user id "2" (which we use to join) is not listed in the topic table you will not get any results for this user.
Hope that helps!
use inner join and distinct
select distinct user_table.id
from user_table
inner join topics_table on topic_table.user_id = user_table.id
select u.id
, u.name
, count(b.topicName)
from user u
left join topic t on t.userid = u.id
group by u.id, u.name
You can select topic number per user and then join it with user data. Something like this:
with t as
(
select userid, count(*) as n
from topic
group by userid
)
SELECT user.*, t.n
FROM user
JOIN t ON user.id = t.userid

SQL Exclude results from join or subquery where column names don't match up with multiple tables

Ok, I'm working with an existing database (cannot edit the tables, columns, etc.) I am asked to create a report with the data for clients. This worked fine until we needed to create an exclusions group for certain clients.
There are 8 tables that need to be parsed for information in the database to execute this query properly. I've "simplified" it to 6 tables as best as I can.
The tables below are the existing tables, in the best order that I could come up with.
Table: Clients
ClientKey ClientNo ClientName
1 12345 ABC
2 12346 DEF
3 12347 GHI
4 12348 JKL
5 12349 MNO
6 12350 PQR
Table: ClientGroup
ClientKey GroupCode GroupValue
12345 EXCLUSIONSGROUP EXCLUDE
12346 EXCLUSIONSGROUP EXCLUDE
12347 OTHERSTUFF SOMETHING
Table: Groups
GroupCode GroupCodeKey
EXCLUSIONSGROUP 25
OTHERSTUFF 14
Table: GroupValues
GroupCode GroupValue
EXCLUSIONSGROUP EXCLUDE
OTHERSTUFF SOMETHING
EXCLUSIONSGROUP SOMETHING
Table: Images
FileKey Filename
987654 NULL
987653 Filename.jpg
987652 Filename.jpg
987651 NULL
987650 NULL
Table: Files
FileKey ClientKey
987654 12345
987653 12345
987652 12346
987651 12347
987650 12347
To better explain these tables:
Clients holds our clients
ClientGroup holds a list of which clients belong to which groups and the value that this client was assigned in that group (clients can be assigned multiple groups, and/or multiple values for a group)
Groups holds a list of the groups that exist as well as the GroupCodeKey. This table is important to refer to because the GroupCode values can change, so referring to '25' for example is the best way to access the proper GroupCode
GroupValues holds a list of all the possible GroupValues that can be assigned to a GroupCode (Group). They may be added, removed, changed.
Images points to the Files table through the FileKey column which points to the Clients table through the ClientKey column. The Images table tells us if a client's file has an image or not (defined by NULL if it does not exist)
Files contains a list of all documents that belong to a client. A client can (and most likely will) have multiple documents/files.
What I need to do:
I need to find all instances where the Filename in the Images table is NULL and where the Client is NOT in the ClientGroup table with a GroupValue of GroupValues(table)GroupValue(column) equal to 'EXCLUDE' and in the GroupCode of Groups(table)GroupCode(column) equal to Groups(table)GroupCodeKey(column) of '25'
In the following code, ignore columns that are unseen in the tables above, they exist, however to simplify the code and tables above, I've removed them from the code. They are still relevant to mention in the code below under select as both queries pull different columns' information from the database which prevents me from doing an EXCEPT between both queries
The current code (simplified) I have to get all the Clients with NULL Images is:
SELECT f.FileKey AS fkey, f.fNo AS fno, f.fDate AS fdate, cli.ClientNo AS clientno, cli.ClientName AS clientname, /*OTHER TABLE STUFF*/
FROM Files as f
LEFT JOIN Images as img
ON f.FileKey=img.FileKey
/*OTHER LEFT JOINS AND TABLES HERE RETURNING OTHER DATA*/
WHERE
img.[Filename] IS NULL
ORDER BY f.FileKey DESC
The current code I have to get all the Clients that are in the group with a GroupCodeKey of '25' and with a GroupValue of 'EXCEPT' is:
SELECT cli.ClientNo AS clientno, cli.ClientName AS clientname
FROM Clients AS cli
LEFT JOIN ClientGroup AS cg
ON cg.ClientKey = cli.ClientKey
LEFT JOIN Groups AS gc
ON gc.GroupCode = cg.GroupCode
LEFT JOIN GroupValue AS gv
ON gv.GroupCode = gc.GroupCode
WHERE
gc.GroupCodeKey='25' AND
gv.GroupValue='EXCLUDE'
Both above queries work exactly as anticipated on their own.
How would I combine these queries to give me the desired output?
The desired output (according to the tables above and their contents) would be to have the information that matches the first query minus the second one:
ClientNo:123457
ClientName:GHI
FileKey:987651
AND
ClientNo:123457
ClientName:GHI
FileKey:987650
Both these results match a client not belonging to the exceptions group '25' with value of 'EXCLUDE' and both FileKeys (987651 and 987650) have Filename set to NULL
I have tried to join all the tables, but cannot seem to properly create the query (I get either no results or I get results for the clients in the exception group only - whereas I need the ones not in the exceptions group). I have also tried creating a subquery, but I couldn't seem to get that to work either...
Any help regarding this is much appreciated.
Thanks!
Not sure if I have all the criteria right, but the general form of what you want can be gotten using NOT EXISTS
SELECT clientno, clientname, fkey, /*OTHER TABLE STUFF*/
FROM (
SELECT f.FileKey AS fkey, f.fNo AS fno, f.fDate AS fdate, cli.ClientNo AS clientno, cli.ClientName AS clientname, /*OTHER TABLE STUFF*/
FROM Files AS f
LEFT JOIN Images AS img
ON f.FileKey=img.FileKey
/*OTHER LEFT JOINS AND TABLES HERE RETURNING OTHER DATA*/
WHERE
img.[Filename] IS NULL
) incl
WHERE NOT EXISTS (
SELECT * FROM(
SELECT cli.ClientNo AS clientno, cli.ClientName AS clientname
FROM Clients AS cli
LEFT JOIN ClientGroup AS cg
ON cg.ClientKey = cli.ClientKey
LEFT JOIN Groups AS gc
ON gc.GroupCode = cg.GroupCode
LEFT JOIN GroupValue AS gv
ON gv.GroupCode = gc.GroupCode
WHERE
gc.GroupCodeKey='25' AND
gv.GroupValue='EXCLUDE'
) excl
WHERE excl.clientno = incl.ClientNo
AND excl.clientname = incl.ClientName
)
ORDER BY fkey DESC
SELECT im.FileKey
FROM Images AS im
INNER JOIN Files as Fi ON im.filekey = fi.filekey
--NOW THAT WE KNOW ALL CLIENT KEYS THAT MEET THE REQUIREMENTS WE NEED TO GO GET THE FILEKEY
INNER JOIN (
-- FIND ALL IMAGES WHERE FILE IS NOT NULL
SELECT F.clientKey
FROM images AS i
INNER JOIN files AS F on f.fileKey = i.fileKey
WHERE i.filename IS NULL
INTERSECT--GRAB THE INTERSECT BECAUSE WE WANT TO KNOW ALL CLIENTKEYS THAT MEET THE BELOW REQUIREMENTS
--FIND ALL CLIENTS THAT ARE NOT IN EXCLUDED AND IN THE GROUPS TABLE WITH GROUPCODE =25
SELECT C.ClientKey
FROM CLIENTS AS C
INNER JOIN CLientGroup AS CG ON C.clientKey = cg.clientkey AND cg.groupvalue != 'EXCLUDE'
INNER JOIN Groups AS g on CG.groupCode = g.groupCode AND g.groupCodeKey = '25') AS x on x.clientkey = fi.clientkey
I believe this will get you what you want(un tested)

Update multiple row values to same row and different columns

I was trying to update table columns from another table.
In person table, there can be multiple contact persons with same inst_id.
I have a firm table, which will have latest 2 contact details from person table.
I am expecting the firm tables as below:
If there is only one contact person, update person1 and email1. If there are 2, update both. If there is 3, discard the 3rd one.
Can someone help me on this?
This should work:
;with cte (rn, id, inst_id, person_name, email) as (
select row_number() over (partition by inst_id order by id) rn, *
from person
)
update f
set
person1 = cte1.person_name,
email1 = cte1.email,
person2 = cte2.person_name,
email2 = cte2.email
from firm f
left join cte cte1 on f.inst_id = cte1.inst_id and cte1.rn = 1
left join cte cte2 on f.inst_id = cte2.inst_id and cte2.rn = 2
The common table expression (cte) used as a source for the update numbers rows in the person table, partitioned by inst_id, and then the update joins the cte twice (for top 1 and top 2).
Sample SQL Fiddle
I think you don't have to bother yourself with this update, if you rethink your database structure. One great advantage of relational databases is, that you don't need to store the same data several times in several tables, but have one single table for one kind of data (like the person's table in your case) and then reference it (by relationships or foreign keys for example).
So what does this mean for your example? I suggest, to create a institution's table where you insert two attributes like contactperson1 and contactperson2: but dont't insert all the contact details (like email and name), just the primary key of the person and make it a foreign key.
So you got a table 'Person', that should look something like this:
ID INSTITUTION_ID NAME EMAIL
1 100 abc abc#inst.com
2 101 efg efg#xym.com
3 101 ijk ijk#fg.com
4 101 rtw rtw#rtw.com
...
And a table "Institution" like:
ID CONTACTPERSON1 CONTACTPERSON2
100 1 NULL
101 2 3
...
If you now want to change the email adress, just update the person's table. You don't need to update the firm's table.
And how do you get your desired "table" with the two contact persons' details? Just make a query:
SELECT i.id, p1.name, p1.email, p2.name, p2.email
FROM institution i LEFT OUTER JOIN person p1 ON (i.contactperson1 = p1.id)
LEFT OUTER JOIN person p2 ON (i.contactperson2 = p2.id)
If you need this query often and access it like a "table" just store it as a view.

Replace id with string in SQL view

I have two tables...
groupid membership_list managerid
-------------------------------------
0 /0//1//2/ 2
1 /2/ 2
userid username
------------------
0 ben
1 tom
2 dan
I'd like to display a table to for example the user 'ben' that is a list of the groups they are a member of that looks like this...
groupid membership_list managername
---------------------------------------
0 /0//1//2/ dan
.. so basically replacing 'manager_id' with the username for that id. I've been hacking away at this but I can't work it out - my SQL skills are clearly a bit lacking - how can I do this?
SELECT groupid, membership_list, managerid FROM blah WHERE membership_list LIKE '%/?/%'
... is about as far as I've got.
SELECT t1.groupid, t1.membership_list, t2.username
FROM table1 t1
INNER JOIN table2 t2 ON t1.managerid = t2.userid
That should do it. Or am I missing something here??
SELECT A.groupid, A.membership_list, B.managername FROM table1 A, table2 B WHERE A.managerid = B.userid and membership_list LIKE '%/?/%'
you need to break out membership_list column into a new table:
changed table: Groups
groupid
managerid
table users
userid
username
new table: UserGroups
groupid
userid
you can then do this:
SELECT
*
FROM Users u
INNER JOIN UserGroups ug On u.userid=ug.userid
INNER JOIN Groups g ON ug.groupid=g.groupid
WHERE u.Name='Ben'
to find all of Ben's groups.
If you don't want to modify your tables, you will need a split function that will convert the multiple values in membership_list into rows. You have not mentioned the actual database you are working on and a split function is dependent on knowing that.