J'ai un peu de SQL qui fait presque ce que je veux faire. Je travaille avec trois tables, utilisateurs, userphonenumbers et userPhonenCumberTypes. J'essaie d'obtenir une liste d'utilisateurs avec leurs numéros de téléphone pour une exportation.
La base de données elle-même est ancienne et a des problèmes d'intégrité. Mon problème est qu'il ne devrait jamais être un type de chaque numéro de téléphone dans la base de données, mais ce n'est pas le cas. Lorsque j'exécute cela, je reçois des résultats multilignes pour chaque personne s'ils contiennent, par exemple, deux chiffres "à la maison".
Comment puis-je modifier le SQL pour prendre le premier numéro de téléphone répertorié et ignorer les numéros restants? Je suis dans SQL Server et je connais la déclaration supérieure. Mais si j'ajoute 'Top 1' à la jointe de gauche, sélectionnez l'instruction, c'est simplement me donner la 1ère entrée de la base de données, pas la 1ère entrée de chaque utilisateur.
Ceci est pour SQL Server 2000.
Merci,
SELECT Users.UserID,
Users.FirstName, Users.LastName,
HomePhone, WorkPhone, FaxNumber
FROM Users
LEFT JOIN
(SELECT UserID, PhoneNumber AS HomePhone
FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
WHERE UserPhoneNumberTypes.PhoneNumberType='Home') AS tmpHomePhone
ON tmpHomePhone.UserID = Users.UserID
LEFT JOIN
(SELECT UserID, PhoneNumber AS WorkPhone
FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
WHERE UserPhoneNumberTypes.PhoneNumberType='Work') AS tmpWorkPhone
ON tmpWorkPhone.UserID = Users.UserID
LEFT JOIN
(SELECT UserID, PhoneNumber AS FaxNumber
FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
WHERE UserPhoneNumberTypes.PhoneNumberType='Fax') AS tmpFaxNumber
ON tmpFaxNumber.UserID = Users.UserID
Chaque fois que vous souhaitez sélectionner seulement une rangée supérieure à partir d'une table de gauche pour chaque ligne Dans la table de droite, vous devez envisager d'utiliser l'opérateur Appliquer au lieu de rejoindre et déplacez la condition de jointure à l'intérieur = la jointure gauche:
SELECT u.UserID,
u.FirstName, u.LastName,
hn.PhoneNumber AS HomePhone
FROM Users u
OUTER APPLY (
SELECT TOP(1) PhoneNumber
FROM UserPhoneNumbers upn
LEFT JOIN UserPhoneNumberTypes upt
ON upn.UserPhoneNumberTypeID=upt.UserPhoneNumberTypeID
WHERE upt.PhoneNumberType='Home'
AND upn.UserID = u.UserID
ORDER BY ...) as hn
...
En supposant que SQL Server 2005+, utilisez Row_Number:
LEFT JOIN (SELECT UserID,
PhoneNumber AS HomePhone,
ROW_NUMBER() OVER (PARTITION BY userid ORDER BY what?) AS rank
FROM UserPhoneNumbers upn
LEFT JOIN UserPhoneNumberTypes upnt ON upnt.UserPhoneNumberTypeID = upn.UserPhoneNumberTypeID
AND upnt.PhoneNumberType='Home') AS tmpHomePhone
ON tmpHomePhone.UserID = Users.UserID
AND tmpHomePhone.rank = 1
Mind the what?
espace réservé pour déterminer le premier numéro. Omettez la commande par si vous ne vous souciez pas du tout ...
Je suppose que vous avez un champ de clé primaire sur chaque table jointe, car l'ID utilisateur n'est pas unique. Je supposerai que votre clé principale s'appelle ID. Nous allons prendre les enregistrements avec l'identifiant le plus bas. Cela répond à vos "premiers" critères.
SELECT Users.UserID, Users.FirstName, Users.LastName, hp.HomePhone,
wp.WorkPhone, fn.FaxNumber
FROM Users
LEFT JOIN HomePhone hp ON hp.UserID = Users.UserID
LEFT JOIN HomePhone hp2 ON hp2.UserID = Users.UserID AND hp2.ID < hp.ID
LEFT JOIN WorkPhone wp ON wp.UserID = Users.UserID
LEFT JOIN WorkPhone wp2 ON wp2.UserID = Users.UserID AND wp2.ID < wp.ID
LEFT JOIN FaxNumber fn ON fn.UserID = Users.UserID
LEFT JOIN FaxNumber fn2 ON fn2.UserID = Users.UserID AND fn2.ID < fn.ID
WHERE hp2.ID IS NULL AND wp2.ID IS NULL AND fn2.ID IS NULL
Il y a tout un chapitre sur ce type de problème, appelé "Gruops ambigu", dans le livre SQL AntiPatterns .
Select Users.UserID, Users.FirstName, Users.LastName
, PhoneNumbers.HomePhone
, PhoneNumbers.WorkPhone
, PhoneNumbers.FaxNumber
From Users
Left Join (
Select UPN.UserId
, Min ( Case When PN.PhoneNumberType = 'Home' Then UPN.PhoneNumber End ) As HomePhone
, Min ( Case When PN.PhoneNumberType = 'Work' Then UPN.PhoneNumber End ) As WorkPhone
, Min ( Case When PN.PhoneNumberType = 'Fax' Then UPN.PhoneNumber End ) As FaxPhone
From UserPhoneNumbers As UPN
Join (
Select Min(UPN1.UserPhoneNumberId) As MinUserPhoneNumberId
, UPNT1.PhoneNumberType
From UserPhoneNumbers As UPN1
Join UserPhoneNumberTypes As UPNT1
On UPNT1.UserPhoneNumberTypeID = UPN1.UserPhoneNumberTypeID
Where UPNT1.PhoneNumberType In('Home', 'Work', 'Fax')
Group By UPN1.UserID, UPNT.PhoneNumberType
) As PN
On PN.MinUserPhoneNumberId = UPN.UserPhoneNumberId
Group By UPN.UserId
) As PhoneNumbers
On PhoneNumbers.UserId = Users.UserId
Dans cette solution, pour chaque type d'utilisateur et de numéro de téléphone, je choisis la valeur de la clé principale la plus basse de la table UserPhoneNumbers
(j'ai deviné que la colonne a été nommée UserPhoneNumberId
).
Vous devez définir ce que vous entendez par "d'abord" lorsqu'il existe deux numéros du même type, puis ajoutez une condition à votre joindre afin que seul le bon enregistrement réponde aux critères. Il n'y a pas d'autre raccourci pour cela.
Tenir, juste pour comprendre la question.
Vous avez deux tables:
Utilisateurs (ID utilisateur -> x) userphones (ID utilisateur, phonetype -> numéro de téléphone) et userID/phonetype n'est pas unique.
Tout d'abord, il n'y a pas besoin de tables Temps:
Select
x
from
Users
inner join
(
Select
top 1 y
from
FoneTypes
where
UserID = users.UseriD
and phoneType = 'typex'
) as PhoneTypex on phonetypex.UserID = users.userID
Ajouter des jointures intérieures si nécessaire.
Ou je manque quelque chose?
Vous pouvez simplement utiliser le groupe par:
SELECT Users.UserID,
Users.FirstName, Users.LastName,
HomePhone, WorkPhone, FaxNumber
FROM Users
LEFT JOIN
(SELECT UserID, min(PhoneNumber) AS HomePhone
FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
WHERE UserPhoneNumberTypes.PhoneNumberType='Home'
GROUP BY userID) AS tmpHomePhone
ON tmpHomePhone.UserID = Users.UserID
LEFT JOIN
(SELECT UserID, min(PhoneNumber) AS WorkPhone
FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
WHERE UserPhoneNumberTypes.PhoneNumberType='Work'
GROUP BY userID) AS tmpWorkPhone
ON tmpWorkPhone.UserID = Users.UserID
LEFT JOIN
(SELECT UserID, min(PhoneNumber) AS FaxNumber
FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
WHERE UserPhoneNumberTypes.PhoneNumberType='Fax'
GROUP BY userID) AS tmpFaxNumber
ON tmpFaxNumber.UserID = Users.UserID
Au lieu de Min (), vous pouvez utiliser Max () aussi.
Ou vous pouvez le faire dans un groupe par:
SELECT Users.UserID,
Users.FirstName, Users.LastName,
max(HomePhone) as HomePhone,
max(WorkPhone) as WorkPhone,
max(FaxNumber) as FaxNumber
FROM Users
LEFT JOIN
(SELECT UserID, PhoneNumber AS HomePhone
FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
WHERE UserPhoneNumberTypes.PhoneNumberType='Home') AS tmpHomePhone
ON tmpHomePhone.UserID = Users.UserID
LEFT JOIN
(SELECT UserID, PhoneNumber AS WorkPhone
FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
WHERE UserPhoneNumberTypes.PhoneNumberType='Work') AS tmpWorkPhone
ON tmpWorkPhone.UserID = Users.UserID
LEFT JOIN
(SELECT UserID, PhoneNumber AS FaxNumber
FROM UserPhoneNumbers LEFT JOIN UserPhoneNumberTypes ON UserPhoneNumbers.UserPhoneNumberTypeID=UserPhoneNumberTypes.UserPhoneNumberTypeID
WHERE UserPhoneNumberTypes.PhoneNumberType='Fax') AS tmpFaxNumber
ON tmpFaxNumber.UserID = Users.UserID