Get data using JOIN - sql

I have two tables having following data-
Social_Tbl
ID Name Value
------------------------
1 Facebook FB
2 Orkut OR
3 Google GL
4 Other OT
And Organization_tbl
ID Organization Name
-----------------------------
1 1234 Facebook
2 1234 Google
3 146 Other
4 126 Other
5 126 Facebook
6 77 Google
Here, 'Name' is the foreign key (Not ID).
I want to join these tables and get the 'Name' columns data which does not belong to organization id 1234. As follows-
Name
----
Orkut
Other
Here, 'Orkut' and 'Other' does not belong to 1234 organization.
I tried following query for this-
select * from Social_Tbl st
join Organization_tbl ot
on st.Name = ot.Name
where Organization = 1234
This query fetches Names related to 1234 i.e Facebook and Google. I want result
Orkut and Other. If I replace Organization = 1234 with Organization != 1234 it returns all data from Organization_tbl.
Can somebody help me on this. This should be pretty simple, just npt able to find it out.

Could be done with a subquery:
select st.Name
from Social_Tbl st
where not exists (
select *
from Organization_tbl ot
where st.Name = ot.Name
and ot.Organization = 1234
)
(This also returns names that don't have an entry in Organization_tbl at all.)

Related

Using TOP 1 (or CROSS APPLY) within multiple joins

I've reviewed multiple Q&A involving TOP 1 and CROSS APPLY (including the very informative 2043259), but I still can't figure out how to solve my issue. If I had a single join I'd be fine, but fitting TOP 1 into the middle of a chain of joins has stumped me.
I have four tables and one of the tables contains multiple matches when joining due to a previous bug (since fixed) that created new records in the table instead of updating existing records. In all cases, where there are multiple records, it is the top-most record that I want to use in one of my joins. I don't have access to the table to clean up the extraneous data, so I just have to deal with it.
The purpose of my query is to return a list of all "Buildings" managed by a particular person (user choses a person's name and they get back a list of all buildings managed by that person). My tables are:
Building (a list of all buildings):
BuildingId BuildingName
1 Oak Tree Lane
2 Lighthoue Court
3 Fairview Lane
4 Starview Heights
WebBuildingMapping (mapping of BuidingId from Building table, that is part of an old system, and corresponding WebBuildingId in another piece of software):
BuildingId WebBuildingId
1 201
2 202
3 203
4 204
WebBuildingContacts (list of ContactID for the building manager of each building). This is the table with duplicate values - where I want to choose the TOP 1. In sample data below, there are two references to WebBuidingId = 203 (row 3 & row 5) - I only want to use row 3 data in my join.
Id WebBuildingId ContactId
1 201 1301
2 202 1301
3 203 1303
4 204 1302
5 203 1302
Contacts (list of ContactIds and corresponding property manager Names)
ContactId FullName
1301 John
1302 Mike
1303 Judy
As noted, in the example above, the table WebBuildingContact has two entries for the building with a WebBuidingId = 203 (row 3 and row 5). In my query, I want to select the top one (row 3).
My original query for a list of buildings managed by 'Mike' is:
SELECT BuildingName
FROM Building bu
JOIN WebBuildingMapping wbm ON wbm.BuildingId = bu.BuildingId
JOIN WebBuildingContact wbc ON wbc.WebBuildingId = wbm.WebBuildingId
JOIN Contacts co ON co.ContactId = wbc.ContactId
WHERE co.FullName = 'Mike'
This returns 'Fairview Lane' and 'Starview Heights'; however, Judy manages 'Fairview Lane' (she's the top entry in the WebBuildingContacts table). To modify the query and eliminate row 5 in WebBuildingContacts from the join, I did the following:
SELECT BuildingName
FROM Building bu
JOIN WebBuildingMapping wbm ON wbm.BuildingId = bu.BuildingId
JOIN WebBuildingContact wbc ON wbc.WebBuildingId =
(
SELECT TOP 1 WebBuildingId
FROM WebBuildingContact
WHERE WebBuildingContact.WebBuildingId = wbm.WebBuildingId
)
JOIN Contacts co ON co.ContactId = wbc.ContactId
WHERE co.FullName = 'Mike'
When I try this; however, I get the same result set (ie it returns 'Mike' as manager for 2 buildings). I've also made various attempts to use CROSS APPLY but I just end up with 'The multi-part identifier could not be bound', which is a whole other rabbit hole to go down.
You could try this:
SELECT bu2.BuildingName
FROM building bu2
WHERE bu2.BuildingId IN
(SELECT MAX(bu.BuildingId)
FROM Building bu
JOIN WebBuildingMapping wbm ON wbm.BuildingId = bu.BuildingId
JOIN WebBuildingContact wbc ON wbc.WebBuildingId = wbm.WebBuildingId
JOIN Contacts co ON co.ContactId = wbc.ContactId
WHERE co.FullName = 'Mike'
);

Using a field to filter a selection on a second field in SQL Server

I have a table ClientContacts, which holds basic information about a pairing of clients. Some of the details held in this table include P1Title, P2Title, P1FirstName, P2FirstName. For each row in this table there may be details of one or two clients, with a CustomerId that represents the pairing. Within this table is also ContactId, which is used to link to the table described below.
In a second table ContactDetails which contains rows that hold a specific contact detail that is associated with a client. Each client may have a number of rows in this table, each row holding a different detail such as HomeNumber, MobileNumber and Email. This table also contains a Type field which represents the type of contact detail held in the row. 1 = Home number, 2 = Mobile number and 3 = email. The Note field is also included, which may hold either Mr or Mrs denoting whether the mobile number held belongs to Person1 or Person2 in the client pairing.
Here is a visual structure of the tables.
ClientContacts
CustomerId ContactId Person1Title Person1FirstName Person1LastName Person2Title Person2FirstName Person2LastName
1 100 Mr Bob BobLastname Mrs Bobette BobetteLastname
2 101 Mr John JohnLastname Mrs Johnette JohnetteLastname
ContactDetails
ContactId Detail Type Note
100 012345 1
100 077777 2 P1
100 012333 1
100 088888 2 P2
101 099999 1
101 012211 1
101 066666 2
101 email#email.com 3
I want to construct a query that allows me to pull back the information of both of the clients, as well as figure out whether any of the mobile numbers stored in the ContactDetails table belongs to either of the two clients, if it does, I need to be able to tell which belongs to Person1 or Person2 in the pairing.
In addition, if the note field is null for a particular mobile number (type = 2), the first mobile number should be used for Person1 and the second should be used for Person2.
Below is my desired output:
Output
CustomerId Person1Firstname
Person1Lastname Person2Firstname Person2Lastname Home Person1Mobile Person2Mobile Person2Email
1 Bob BobLastname Bobette BobetteLastname 012211 077777 088888 null
I have a partially working query that manages to extract the mobile numbers and relates them to P1 or P2, however this only works if the Note field is not null.
select
cc.CustomerId,
cc.Person1Forename,
cc.Person1Surname,
cc.Person2Forename,
cc.Person2Surname,
max(case when cd.Type = 3 then cd.Detail end) as 'Home',
max(case when cd.Type = 4 and cd.Note = cc.P1Title then cd.Detail end) as 'Person1Mobile',
max(case when cd.Type = 4 and cd.Note = cc.P2Title then cd.Detail end) as 'Person2Mobile',
max(case when cd.Type = 5 then cd.Detail end) as 'Email'
from ClientContacts cc join
ContactDetails
cd on cc.ContactId = cd.ContactId
I'm unsure how to proceed from here. Any help would be appreciated.

Tough SQL Update

2 databases QF AND TK
QF has the following:
Imagine you have a table called FunctionalGroup with this data:
FunctionalGroupID | FunctionalGroup
1 Engineering
2 Purchasing
And a table that was a set of login's with a functionalgroupID to reference the group the person is in...
LoginID | FunctionalGroupID | Login
1 1 Jon
2 1 Joe
3 2 Jane
So Jon and Joe are engineering while Jane is purchasing..simple enough
Now there is another database TK.
TK has the following table Login with something to this effect:
Login | FunctionalGroupID
Jon Purchasing
Joe Purchasing
Jane Purchasing
Notice how Jon and Joe in this database are now part of the purchasing group...But notice how this field is the text field and no ID. So what I want to do is use this table as the master data source and update the QF table such that the logins table from the QF now looks like this:
LoginID | FunctionalGroupID | Login
1 2 Jon
2 2 Joe
3 2 Jane
That is update this table to make Jon and Joe part of the purchasing group by setting their functionalgroupid = 2. Because 2 means purchasing.
I tried this:
UPDATE
Login
SET Login.FunctionalGroupID = FunctionalGroup.FunctionalGroupID
FROM Login INNER JOIN
TKKCommonData.dbo.Login lz
ON lz.Login = Login.Login
AND lz.FunctionalGroupID = FunctionalGroup.FunctionalGroup
But I get an error:
Msg 4104, Level 16, State 1, Line 1
The multi-part identifier "FunctionalGroup.FunctionalGroup" could not be bound.
This seems so easy but Im just not sure how to write the update statement. Im just looking to join the tables by the Login (which is the users name) and then by the Functionalgroup names.
I even tried this EDIT per Jay's answer with same error message
UPDATE
QuikFix.dbo.Login
SET QuikFix.dbo.Login.FunctionalGroupID = QuikFix.dbo.FunctionalGroup.FunctionalGroupID
FROM QuikFix.dbo.Login INNER JOIN
TKKCommonData.dbo.Login
ON TKKCommonData.dbo.Login.Login = QuikFix.dbo.Login.Login
AND TKKCommonData.dbo.Login.FunctionalGroupID = QuikFix.dbo.FunctionalGroup.FunctionalGroup
WHERE TKKCommonData.dbo.Login.LoginID= 101
You need an additional INNER JOIN:
UPDATE Login
SET
Login.FunctionalGroupID = FunctionalGroup.FunctionalGroupID
FROM Login
INNER JOIN TKKCommonData.dbo.Login lz
ON lz.Login = Login.Login
INNER JOIN FunctionalGroup
ON lz.FunctionalGroupID = FunctionalGroup.FunctionalGroup
Specify the database name for all of the tables in your query instead of just TKKCommonData.dbo.Login, seems like it can't find the FunctionalGroup table in the database the query is running against.

sybase - values from one table that aren't on another, on opposite ends of a 3-table join

Hypothetical situation: I work for a custom sign-making company, and some of our clients have submitted more sign designs than they're currently using. I want to know what signs have never been used.
3 tables involved:
table A - signs for a company
sign_pk(unique) | company_pk | sign_description
1 --------------------1 ---------------- small
2 --------------------1 ---------------- large
3 --------------------2 ---------------- medium
4 --------------------2 ---------------- jumbo
5 --------------------3 ---------------- banner
table B - company locations
company_pk | company_location(unique)
1 ------|------ 987
1 ------|------ 876
2 ------|------ 456
2 ------|------ 123
table C - signs at locations (it's a bit of a stretch, but each row can have 2 signs, and it's a one to many relationship from company location to signs at locations)
company_location | front_sign | back_sign
987 ------------ 1 ------------ 2
987 ------------ 2 ------------ 1
876 ------------ 2 ------------ 1
456 ------------ 3 ------------ 4
123 ------------ 4 ------------ 3
So, a.company_pk = b.company_pk and b.company_location = c.company_location. What I want to try and find is how to query and get back that sign_pk 5 isn't at any location. Querying each sign_pk against all of the front_sign and back_sign values is a little impractical, since all the tables have millions of rows. Table a is indexed on sign_pk and company_pk, table b on both fields, and table c only on company locations. The way I'm trying to write it is along the lines of "each sign belongs to a company, so find the signs that are not the front or back sign at any of the locations that belong to the company tied to that sign."
My original plan was:
Select a.sign_pk
from a, b, c
where a.company_pk = b.company_pk
and b.company_location = c.company_location
and a.sign_pk *= c.front_sign
group by a.sign_pk having count(c.front_sign) = 0
just to do the front sign, and then repeat for the back, but that won't run because c is an inner member of an outer join, and also in an inner join.
This whole thing is fairly convoluted, but if anyone can make sense of it, I'll be your best friend.
How about something like this:
SELECT DISTINCT sign_pk
FROM table_a
WHERE sign_pk NOT IN
(
SELECT DISTINCT front_sign sign
FROM table_c
UNION
SELECT DISTINCT rear_sign sign
FROM table_c
)
ANSI outer join is your friend here. *= has dodgy semantics and should be avoided
select distinct a.sign_pk, a.company_pk
from a join b on a.company_pk = b.company_pk
left outer join c on b.company_location = c.company_location
and (a.sign_pk = c.front_sign or a.sign_pk = c.back_sign)
where c.company_location is null
Note that the where clause is a filter on the rows returned by the join, so it says "do the joins, but give me only the rows that didn't to join to c"
Outer join is almost always faster than NOT EXISTS and NOT IN
I would be tempted to create a Temp table for the inner join and then outer join that.
But it really depends on the size of your data sets.
Yes, the schema design is flawed, but we can't always fix that!

Compare 2 values of different types inside of subquery

I am using a MS SQL db and I have 3 tables: 'base_info', 'messages', 'config'
bases:
ID Name NameNum
====================================
1 Home 101
2 Castle 102
3 Car 103
messages:
ID Signal RecBy HQ
============================
111 120 Home 1
111 110 Castle 1
111 125 Car 1
222 120 Home 2
222 125 Castle 2
222 130 Car 2
333 100 Home 1
333 110 Car 2
config:
ID SignalRec SignalOut RecBy HQ
====================================
111 60 45 101 1
111 40 60 102 1
222 50 60 102 2
222 30 90 101 2
333 80 10 103 1
Ok so now I have a subquery in which I select the 'SignalRec' and 'SignalOut' from the config table and match it on the messages table by ID and Date(not included above), the problem is that I need it to match where messages.RecBy = config.RecBy but config.RecBy is a string but it's equivalent Name is in the bases table. So I almost need to do a subquery inside a subquery or some type of join and compare the returned value.
Here is what I have so far:
(SELECT TOP 1 config.SignalRec from config WHERE config.ID = messages.ID AND ||I need th other comparison here||...Order By...) As cfgSignalRec,
(SELECT TOP 1 config.SignalOut from config WHERE config.ID = messages.ID AND ||I need th other comparison here||...Order By...) As cfgSignalOut
I tried to make this as clear as possible but if you need more info let me know.
I would normalize out RecBy in your messages table to reference the bases table. Why would you insert the string content there if it's also referenced in bases?
This is exactly why normalization exists: reduce redundancy, reduce ambiguity, and enforce referential integrity.
To make this more clear, RecBy in the messages table should be a foreign key to Bases.
I think this could do the trick (although I have not tried it...)
SELECT
c.SignalRec
FROM config c
INNER JOIN bases b
ON c.RecBy = b.NameNum
INNER JOIN messages m
ON b.Name = m.RecBy
WHERE c.ID = m.ID
However, as Anthony pointed out, you probably want to normalize out the strings in the RecBy column in the messages table, as you have the same data in the bases table.
From your description, it just sounds like you need two JOINS
SELECT TOP 1
c.SignalRec
FROM
config c
INNER JOIN
bases b
ON c.RecBy = b.NameNum
INNER JOIN
messages m
ON b.Name = m.RecBy
I think I might have not been clear enough what I wanted to do, sorry about that.
The data is actually different in the 2 tables, although the correlations are the same. It's kind of confusing to explain without going into detail about how the system works.
I actually found a very fast way of doing this.
Inside my sub-query I do this:
(SELECT TOP 1 config.Signal FROM config,bases
WHERE config.ID = messages.ID AND bases.Name = messages.RecBy AND bases.NameNum =
config.RecBy Order By...)
So this essentially compares the 2 RecBy's of different tables even though one is an integer and the other is a string. It reminds me of a match and look up in Excel.