Как использовать подзапросы для выбора максимальных значений? - PullRequest
1 голос
/ 06 ноября 2019

Database

Я пытаюсь создать сложный запрос SQL Server для выбора конкретных данных. Во-первых, позвольте мне дать вам некоторую справочную информацию ...

Я использую базу данных, в которой мне нужно достичь 3 таблиц, используя этот запрос. Вы можете увидеть диаграмму на картинке. Как видите, в этой базе данных есть пользователи. Эти пользователи могут купить подписку, чтобы использовать некоторые общественные велосипеды по городу. Подписка имеет тип подписки. Этот тип подписки объявляет, как долго действительна подписка. Это может быть на день, месяц или год. Пользователь может иметь несколько подписок.

В моем запросе мне нужно выбрать идентификатор пользователя, имя, адрес, состоящий из улицы, номера, почтового индекса, города и кода страны, а также описание типа подписки. Для каждого пользователя мне нужна только самая длинная подписка. поэтому вам нужно отфильтровать запрос по subscriptionTypeId DESC. но не только это ... у пользователя может быть несколько подписок одного и того же типа, поэтому мне нужна только самая последняя. Это означает, что я также должен фильтровать ValidFrom DESC, самый длинный во время подписки.

Я знаю, что для этого мне нужно использовать подзапрос, но, похоже, не могу найти достойный запрос, который работает ... Это то, как далеко я продвинулся, но я использовал несколько методов, чтобы попытатьсяполучите то, что мне нужно:

SELECT 
    su.userId, u.name, u.street, u.number, u.zipcode, u.CountryCode, st.description, 
    MAX(su.validFrom) AS MaxValidFrom, 
    (SELECT MAX(SubscriptionTypeId) FROM dbo.Subscriptions 
     WHERE UserId = su.UserId) AS MaxSubscriptionTypeId
FROM 
    dbo.Users u 
INNER JOIN 
    dbo.Subscriptions su ON u.userId = su.userId
INNER JOIN 
    dbo.SubscriptionTypes st ON su.subscriptionTypeId = st.subscriptionTypeId
HAVING 
    su.SubscriptionTypeId = MAX(su.SubscriptionTypeId)
ORDER BY 
    su.UserId, MaxValidFrom DESC;

Заранее спасибо за помощь!

Joren

Ответы [ 2 ]

0 голосов
/ 06 ноября 2019

SQL может быть немного привязан к встроенным подзапросам в зависимости от индексов мощности и ресурсов и т. Д.

Я бы избегал пользовательских функций, поскольку он почти всегда отрицательно влияет на план запроса.

Разбейте его, и вы получите максимум (SubscriptionTypeId), который когда-либо имел пользователь, вместе с последним экземпляром пользователя, подписанного на этот TypeId.

SELECT 
    u.userId, u.name, u.street, u.number, u.zipcode, u.CountryCode
    --,st.description 
    --,MAX(su.validFrom) AS MaxValidFrom, 
    --,(SELECT MAX(SubscriptionTypeId) FROM dbo.Subscriptions WHERE UserId = su.UserId) AS MaxSubscriptionTypeId
into #temp
FROM 
    dbo.Users u 
WHERE EXISTS (SELECT top 1 1 from dbo.Subscriptions i_su where i_su.UserId = u.UserId)
--Now you have a list of all who have at least 1 subscription inside a temp table.

Select u.*
    ,st.description
    ,su.ValidFrom
    ,su.SubscriptionTypeId
    --This function will return a "Rank" for each user (partition) in order of the "Order By" clause.
    ,ROW_NUMBER() OVER (partition by u.UserId order by SubscriptionTypeId DESC, ValidFrom DESC) as RowNo
into #temp2
FROM #temp u
INNER JOIN dbo.Subscriptions su
ON u.userId = su.userId
INNER JOIN dbo.SubscriptionTypes st
ON su.subscriptionTypeId = st.subscriptionTypeId

SELECT *
FROM #temp2
where RowNo = 1
-- RowNo 1 has the data you want based on the rank.

if (object_id('temptb..#temp') IS NOT NULL)
    DROP TABLE #temp
if (object_id('temptb..#temp2') IS NOT NULL)
    DROP TABLE #temp2
0 голосов
/ 06 ноября 2019

Joren,

попробуйте вызов функции масштабирования, подобный этому

CREATE FUNCTION MaxSubscriptionTypeID

(   @UserId int ) RETURNS int AS BEGIN  DECLARE @ID int     SELECT @ID = MAX(SubscriptionTypeId) FROM dbo.Subscriptions 
     WHERE UserId = @UserID     -- Return the result of the function    RETURN idnull(@ID,0)

END GO

SELECT 
    su.userId, u.name, u.street, u.number, u.zipcode, u.CountryCode, st.description, 
    MAX(su.validFrom) AS MaxValidFrom, 
    dbo.MaxSubscriptionTypeID( su.UserId) AS MaxSubscriptionTypeId FROM 
    dbo.Users u  INNER JOIN 
    dbo.Subscriptions su ON u.userId = su.userId INNER JOIN 
    dbo.SubscriptionTypes st ON su.subscriptionTypeId = st.subscriptionTypeId HAVING 
    su.SubscriptionTypeId = MAX(su.SubscriptionTypeId) ORDER BY 
    su.UserId, MaxValidFrom DESC;
...