如何根据MAX(TransactionDate)消除行?

时间:2014-06-26 17:41:50

标签: sql sql-server tsql sql-server-2012

我有一个复杂的查询,在select中有很多列,还有很多连接。 2个主要表(不是唯一的表)是SalesDocumentItems和Transactions。我只想要具有唯一SalesDocumentItemIDs的记录,并且我愿意通过仅使用MAX(TransactionDate)连接匹配的Transactions记录来实现此目的。这是一个例子:

select * 
from @Financials f1
where f1.TransactionDate = (
    select MAX(TransactionDate)
    from @Financials
    where SalesDocumentItemID = f1.SalesDocumentItemID
)

该查询实际上完美无缺,完全符合我的需要,但在UDF中填充@Financials已证明不可能实现。事实上,我有另一个关于该主题的堆栈问题:

How to return a table variable from a function (UDF)?

无论其他问题的结果如何,我仍然想知道如何在不借助表变量的情况下做到这一点。我也认为GROUP BY不是一个好的选择,因为在选择和大量的连接中有很多列,并且它看起来太毛茸茸而不能走这条路。

**以下编辑**

我包括我原来的选择声明。我真的需要一点帮助,如何将你们已经显示的方法集成到我当前的查询中。不知道如何连接这两个。非常感谢。

Select [a bunch of columns, including SalesDocumentItemID and TransactionDate]

    From
        (   Select [a bunch of Transaction columns]
            From Transactions trx_detail
            Where trx_detail.TransactionStatusID = 1
                and trx_detail.TransactionDate >= @StartDate
                and trx_detail.TransactionDate < DATEADD(dd,1,@EndDate) 
        ) t
        inner join gym.Account acct on t.AccountID = acct.AccountID
        inner join gym.SalesDocumentItemTransaction linker on linker.TransactionId = t.TransactionID    --do not need to aggregate because there is only ever one set of sales docs items when looking from the transaction's perspective
        inner join gym.SalesDocumentItems docitems on linker.SalesDocumentItemID = docitems.SalesDocumentItemID
        inner join gym.Product prod on docitems.ProductID = prod.ProductID
        left join gym.ProductGroup pgroup on prod.ProductGroupID = pgroup.ProductGroupID
        left join gym.RevenueGroup rgroup on pgroup.RevenueGroupID = rgroup.RevenueGroupID
        left join gym.[Site] home on acct.SiteID = home.SiteID
        left join gym.[Person] collector on t.CollectorPersonID = collector.PersonID
        left join gym.[Registration] reg on reg.RegistrationID = t.RegistrationID
        left join gym.[Site] trx_site on trx_site.SiteID = reg.SiteID
        left join gym.TransactionStatus tstatus on t.TransactionStatusID = tstatus.TransactionStatusID
Where (@SiteID = 0 OR (@SiteID <> 0 and docitems.RevenueSiteID = @SiteID))

数据示例:

SalesDocumentID  TransactionDate
1020557          2014-06-25 16:44:01.930
1020557          2014-06-25 16:44:17.557
1020557          2014-06-25 16:44:33.210
1020558          2014-06-25 16:44:50.007
1020558          2014-06-25 16:44:33.210
1020559          2014-06-25 16:44:50.007

这是我最终的非编译查询,基于Blam的建议:

select tjoin.*
    from (
      Select t.SalesDocumentItemID, t.TransactionDate, SeqNum=row_number() 
        over (partition by t.SalesDocumentItemID 
        order by t2.TransactionDate desc)
        From
        (   --Filter Transactions First for optimization purposes, prior to joining to the linker table
            Select
        --Transaction Information
        t.TransactionID
        ,t.AccountID
        ,t.AccountContractID
        ,t.SalesDocumentID
        ,docitems.SalesDocumentItemID
        ,t.CollectorPersonID
        ,trx_site.SiteID as CollectedSiteID
        ,t.SalesPersonID
        ,t.PaymentMethodID
        ,t.CCnumber
        ,t.TransactionStatusID
        ,t.TransactionDate
        ,t.TransactionAmount
        ,t.APIResponseCode
        ,t.APIResponseReason
        ,t.RegistrationID
        ,t.TransactionTypeID
        ,year(t.TransactionDate) as TransactionYear
        ,month(t.TransactionDate) as TransactionMonth
        ,datepart(ww, t.TransactionDate) as TransactionWeek
        ,day(t.TransactionDate) as TransactionDay

        --Trx Type Breakdown TransactionTypeID  Name; 1 BillingProcess; 2   POS; 3  PaymentOnAccount; 4 Refund
        ,case when t.TransactionTypeID = 1 then linker.Amount else 0 end as BillingProcess
        ,case when t.TransactionTypeID = 2 then linker.Amount else 0 end as PointOfSale
        ,case when t.TransactionTypeID = 3 then linker.Amount else 0 end as PaymentOnAccount
        ,case when t.TransactionTypeID = 4 then linker.Amount else 0 end as Refund

        ,linker.Amount as LineItemAmount

        --Product Information
        ,docitems.ProductID 
        ,docitems.Amount 
        ,docitems.Quantity 
        ,docitems.TaxAmount 
        ,docitems.TotalAmount 
        ,docitems.AmountPaid
        ,docitems.RevenueSiteID
        ,prod.ProductName
        ,prod.ProductGroupID
        ,pgroup.ProductGroup
        ,pgroup.RevenueGroupID --added for new available field 17 May 2014
        ,rgroup.RevenueGroup 

        --Account Info (Home Site for non-inventoried products filter - read: repetitives)
        ,acct.IsActive
        ,acct.SiteID
        ,home.SiteName

        --Collector Info
        ,gym.Person__FormatName(collector.FirstName, collector.LastName, collector.MiddleName, collector.NickName) as CollectorName

        --Collected Site Info (Sale Site for inventoried products filter - read: merchandise)
        ,trx_site.SiteName as CollectedSiteName

        --Transaction Status Info
        ,tstatus.Name as TransactionStatus  

    From
        (   --Filter Transactions First for optimization purposes, prior to joining to the linker table
            Select
                trx_detail.TransactionID
                ,trx_detail.AccountID
                ,trx_detail.AccountContractID
                ,trx_detail.SalesDocumentID
                ,trx_detail.CollectorPersonID
                ,trx_detail.SalesPersonID
                ,trx_detail.PaymentMethodID
                ,trx_detail.TransactionStatusID
                ,trx_detail.TransactionDate
                ,trx_detail.TransactionAmount
                ,trx_detail.APIResponseCode
                ,trx_detail.APIResponseReason
                ,trx_detail.TransactionTypeID
                ,trx_detail.RegistrationID
                ,trx_detail.CCNumber
            From
                gym.[Transaction] trx_detail
            Where
                trx_detail.TransactionStatusID = 1  --successful transactions only
                and trx_detail.TransactionDate >= @StartDate
                and trx_detail.TransactionDate < DATEADD(dd,1,@EndDate) --need to add 1 day to account for the Time portion of the TransactionDate.  Notice it is simply Less Than (no equal).
        ) t
        inner join gym.Account acct on t.AccountID = acct.AccountID
        inner join gym.SalesDocumentItemTransaction linker on linker.TransactionId = t.TransactionID    --do not need to aggregate because there is only ever one set of sales docs items when looking from the transaction's perspective
        inner join gym.SalesDocumentItems docitems on linker.SalesDocumentItemID = docitems.SalesDocumentItemID
        inner join gym.Product prod on docitems.ProductID = prod.ProductID
        left join gym.ProductGroup pgroup on prod.ProductGroupID = pgroup.ProductGroupID
        left join gym.RevenueGroup rgroup on pgroup.RevenueGroupID = rgroup.RevenueGroupID
        left join gym.[Site] home on acct.SiteID = home.SiteID
        left join gym.[Person] collector on t.CollectorPersonID = collector.PersonID
        left join gym.[Registration] reg on reg.RegistrationID = t.RegistrationID
        left join gym.[Site] trx_site on trx_site.SiteID = reg.SiteID
        left join gym.TransactionStatus tstatus on t.TransactionStatusID = tstatus.TransactionStatusID

    Where (@SiteID = 0 OR (@SiteID <> 0 and docitems.RevenueSiteID = @SiteID))) tjoin
  where tjoin.SeqNum = 1

2 个答案:

答案 0 :(得分:2)

如果正确使用,

GROUP BY并不是一件坏事!在这种情况下,窗口函数可能是更好的选择。这将从每个SalesDocumentItemID的交易中获得1条记录:

select t.*
from (
    select t2.*, SeqNum=row_number() over (partition by SalesDocumentItemID order by SomeSensibleField)
    from Transactions t2
) t
where SeqNum = 1

注意与row_number()函数一起使用的ORDER BY。您需要指定如何为每个SalesDocumentItemID选择所需的记录。例如。如果你想要最新的,你可以使用&#34;(按SalesDocumentID分区按SomeDateColumn desc 排序)&#34;或任何适合您需要的东西。

更仔细地检查窗口函数。它们非常强大。

答案 1 :(得分:1)

至于退回一张桌子 How do I "Declare the scalar variable" in a VIEW in Sql Server (2005)

为什么你觉得你需要一个表变量?
我假设这两个表是SalesDocumentItems和Transactions

select tjoin.*
from (  select *, SeqNum=row_number() over (partition by t1.SalesDocumentItemID 
                                                order by t2.TransactionDate desc)
          from SalesDocumentItems t1 
          join Transactions t2
            on t1.SalesDocumentItemID = t2.SalesDocumentItemID
     ) tjoin
where tjoin.SeqNum = 1

如果SalesDocumentItems确实是一个复杂的查询,那么只需使用cte

WITH SalesDocumentItems (SalesDocumentItemID, TransactionDate, ...)
AS
(
    SELECT ...
)
    select tjoin.*
    from (  select *, SeqNum=row_number() over (partition by t1.SalesDocumentItemID 
                                                    order by t2.TransactionDate desc)
              from SalesDocumentItems t1 
              join Transactions t2
                on t1.SalesDocumentItemID = t2.SalesDocumentItemID
         ) tjoin
    where tjoin.SeqNum = 1

你这样做比你需要的更难

select tjoin.*
    from (
      Select t.SalesDocumentItemID, tTransactionDate
           , SeqNum=row_number() over (partition by t.SalesDocumentItemID 
                                           order by t2.TransactionDate desc)
        From Transactions t
        inner join gym.Account acct 
           on t.AccountID = acct.AccountID
          and t.TransactionStatusID = 1
          and t.TransactionDate >= @StartDate
          and t.TransactionDate < DATEADD(dd,1,@EndDate)
        inner join gym.SalesDocumentItemTransaction linker 
           on linker.TransactionId = t.TransactionID    
        inner join gym.SalesDocumentItems docitems 
           on linker.SalesDocumentItemID = docitems.SalesDocumentItemID
        inner join gym.Product prod 
           on docitems.ProductID = prod.ProductID
          and (@SiteID = 0 OR docitems.RevenueSiteID = @SiteID)
         left ..... does note effect the core question
        inner join Transactions t2
           on t1.SalesDocumentItemID = t2.SalesDocumentItemID ) tjoin
  where tjoin.SeqNum = 1