3

Good evening,

I have a table layout similar to the image below. The User table is storing most of my information. For each user I can also have an undetermined number of Preference and Location selections, which I store in junction tables as shown in the image.

Data table layout

  1. When an administrator comes to produce a report, they will use the preference and location selections like a filter. For instance, they may select Preference1 and Preference3, and UserLocation2 and UserLocation4, and the report should show only users who have at least one of the preferences AND at least one of the locations. Is it possible to do this in one SQL select statement? I'm using SQL Server 2008 R2.

  2. Assuming a User record qualifies for return according to the above, is there a way to show all of their Preferences and Locations, concatenated into a new column. I have figured out how to get a concatenated result back for an individual user, but wonder if it can also be done as part of the same SQL select statement.

This is what I have for returning the Preferences comma delimited, for an individual UserID...

DECLARE @concatlist VARCHAR(MAX)
SELECT @concatlist = COALESCE(@concatlist+', ', '') + dbo.Preference.Title
FROM dbo.UserPreference
LEFT OUTER JOIN dbo.Preference 
ON dbo.UserPreference.PreferenceID = dbo.Preference.PreferenceID
WHERE dbo.UserPreference.UserID = 5
SELECT @concatlist as OutputColumn

Any pointers would be great. I've spent a half day on this and though I can do it in part, can't find a way to do it in one statement.

Thanks,

4

2 回答 2

2

You may try this

WITH UPreference AS (
    SELECT  u.*,p.*
    FROM    Users u
    INNER JOIN UserPreference up
      ON u.UserID = up.UserID
    INNER JOIN Preference p
      ON up.PreferenceID = p.PreferenceID
) 
, ULocation AS   (
    SELECT  u.*,l.*
    FROM    Users u
    INNER JOIN UserLocation ul
      ON u.UserID = ul.UserID
    INNER JOIN Location l
      ON l.LocationID = ul.LocationID
)
SELECT  u.*
    ,AllPreferences = STUFF((SELECT ',' + up.Title FROM UPreference up WHERE up.UserID = u.UserID FOR XML PATH('')) ,1,1,'')
    ,AllLocations = STUFF((SELECT ',' + ul.[Name] FROM ULocation ul WHERE ul.UserID = u.UserID FOR XML PATH('')) ,1,1,'')
FROM    Users u 
WHERE 
  EXISTS(SELECT 1 FROM UPreference up WHERE up.UserID = u.UserID AND up.Title IN ('Preference1','Preference3')
AND 
  EXISTS(SELECT 1 FROM ULocation ul WHERE ul.UserID = u.UserID AND ul.[Name] IN ('Location2','Location4')
于 2013-10-28T19:27:38.820 回答
1

If you would like to use XML datatype to represent your selected preferences/locations, then the answer should be simple.

I believe this should solve your first problem:

DECLARE 
    @prefs xml = '<a><i id="1" /><i id="3" /></a>',
    @locs xml = '<a><i id="2" /><i id="3" /></a>'

;WITH p AS (
    SELECT  
        id = c.value('./@id', 'int')
    FROM @prefs.nodes('/a/i') T(c)  
)
,l AS (
    SELECT  
        id = c.value('./@id', 'int')
    FROM @locs.nodes('/a/i') T(c)  
)
SELECT *
FROM 
    [User] u
WHERE 
    u.UserID IN (
        SELECT up.UserID 
        FROM UserPreference up JOIN p ON p.id = up.PreferenceID)
    AND
    u.UserID IN (
        SELECT ul.UserID 
        FROM UserLocation ul JOIN l ON l.id = ul.LocationID)

If the WHERE clause looks too ugly, you can remove superflouos UserID reference and replace it with the following:

WHERE
    u.UserID IN (
        SELECT up.UserID 
        FROM UserPreference up JOIN p ON p.id = up.PreferenceID
        INTERSECT
        SELECT ul.UserID 
        FROM UserLocation ul JOIN l ON l.id = ul.LocationID)

P.S. I've completely lost interest for TVF, CSV and other collection-based input formats and currently am using only XML for it.

于 2013-10-28T20:02:20.043 回答