选择使用UNION返回重复项

时间:2013-05-27 01:15:47

标签: sql tsql

此观点运作良好。它用于与SQLServer 2008 R2交谈的报表构建向导

CREATE view vwQry_LicencePayments 
as

select
P.*,
LT.LicenceTypeDesc, FT.FeeTypeDesc, FT.ThroughputText, FT.Prorata as IsProrata, PT.PaymentStatusDesc, PT.IsPaid,
E.EntityType as HolderEntityType, E.EntityTypeDesc as HolderEntityTypeDesc, E.PersonTitle, E.PersonFirstname, E.PersonSurname, 
E.OrganisationName, E.FullName as HolderFullName, 
E.MailAddress as HolderMailAddress, E.Suburb as HolderSuburb, E.State as HolderState, E.Postcode as HolderPostcode, 
E.Mobile as HolderMobile, E.Email, E.PFN as HolderPFN
from
rptPayment P
inner join vwQry_xhsEntity_Base E on P.HolderHistoryID = E.HistoryID
inner join LicenceType LT on P.LicenceTypeID=LT.LicenceTypeID
inner join LicenceFeeType FT on P.FeeTypeID=FT.FeeTypeID
inner join PaymentStatusType PT on P.PaymentStatusID = PT.PaymentStatusID 

union ALL

select
P.*,
LT.LicenceTypeDesc, FT.FeeTypeDesc, FT.ThroughputText, FT.Prorata as IsProrata, PT.PaymentStatusDesc, PT.IsPaid,
E.EntityType as HolderEntityType, E.EntityTypeDesc as HolderEntityTypeDesc, E.PersonTitle, E.PersonFirstname, E.PersonSurname, 
E.OrganisationName, E.FullName as HolderFullName, 
E.MailAddress as HolderMailAddress, E.Suburb as HolderSuburb, E.State as HolderState, E.Postcode as HolderPostcode, 
E.Mobile as HolderMobile, E.Email, E.PFN as HolderPFN
from 
(select * from rptPayment where HolderHistoryID is null) P
inner join vwQry_Entity E on P.HolderID = E.EntityID
inner join LicenceType LT on P.LicenceTypeID=LT.LicenceTypeID
inner join LicenceFeeType FT on P.FeeTypeID=FT.FeeTypeID
inner join PaymentStatusType PT on P.PaymentStatusID = PT.PaymentStatusID

出现了包含付款日期(LP.dateTimeStamp)的要求。因此,添加额外字段(LP.dateTimeStamp)和两个连接(licenceFeePayment tble和licencePayment tble)必然会导致结果集中出现重复。我假设,例如,如果已经在三次单独付款中支付了许可证,那么它将在licencefeePayment表中有三条记录。他们改变我做的是:

CREATE view vwQry_LicencePayments 
as

select
P.*,
LT.LicenceTypeDesc, FT.FeeTypeDesc, FT.ThroughputText, FT.Prorata as IsProrata, PT.PaymentStatusDesc, PT.IsPaid,
E.EntityType as HolderEntityType, E.EntityTypeDesc as HolderEntityTypeDesc, E.PersonTitle, E.PersonFirstname, E.PersonSurname, 
E.OrganisationName, E.FullName as HolderFullName, 
E.MailAddress as HolderMailAddress, E.Suburb as HolderSuburb, E.State as HolderState, E.Postcode as HolderPostcode, 
E.Mobile as HolderMobile, E.Email, E.PFN as HolderPFN, LP.DatetimeStamp AS  DatetimeStamp
from
rptPayment P
inner join vwQry_xhsEntity_Base E on P.HolderHistoryID = E.HistoryID
inner join LicenceType LT on P.LicenceTypeID=LT.LicenceTypeID
inner join LicenceFeeType FT on P.FeeTypeID=FT.FeeTypeID
inner join PaymentStatusType PT on P.PaymentStatusID = PT.PaymentStatusID 
       inner join licenceFeePayment LFP on P.licenceID = LFP.licenceID
       inner join licencePayment LP on LFP.paymentID = LP.paymentID

union ALL

select
P.*,
LT.LicenceTypeDesc, FT.FeeTypeDesc, FT.ThroughputText, FT.Prorata as IsProrata, PT.PaymentStatusDesc, PT.IsPaid,
E.EntityType as HolderEntityType, E.EntityTypeDesc as HolderEntityTypeDesc, E.PersonTitle, E.PersonFirstname, E.PersonSurname, 
E.OrganisationName, E.FullName as HolderFullName, 
E.MailAddress as HolderMailAddress, E.Suburb as HolderSuburb, E.State as HolderState, E.Postcode as HolderPostcode, 
E.Mobile as HolderMobile, E.Email, E.PFN as HolderPFN, LP.DatetimeStamp AS  DatetimeStamp
from 
(select * from rptPayment where HolderHistoryID is null) P
inner join vwQry_Entity E on P.HolderID = E.EntityID
inner join LicenceType LT on P.LicenceTypeID=LT.LicenceTypeID
inner join LicenceFeeType FT on P.FeeTypeID=FT.FeeTypeID
inner join PaymentStatusType PT on P.PaymentStatusID = PT.PaymentStatusID
       inner join licenceFeePayment LFP on P.licenceID = LFP.licenceID
       inner join licencePayment LP on LFP.paymentID = LP.paymentID

非常感谢帮助。如何加入这两个表并避免重复?

正如@registered用户指出的那样,尽管我已经这样做了,但它并不像从我的UNION中删除ALL那么简单。

@registered用户感谢您的回复。我已根据您的帮助对我的查询进行了更改,现在又有了一个小问题。我添加了一个名为'PaymentTypeDesc'的字段,该字段来自'paymentType'表。由于此表通过'LicencePayment'加入,我必须将它放在您的子查询中。我仍然得到一些重复,因为我现在相信,不仅可以多次支付许可证,它们可以来自多个不同来源EG EFTPOS,CASH等......

我如何才能满足多种付款交易以及多种付款方式?以下是使用您的帮助后我改变的工作查询:

CREATE view vwQry_LicencePaymentsNew 
as

select
P.*,
LT.LicenceTypeDesc, FT.FeeTypeDesc, FT.ThroughputText, FT.Prorata as IsProrata, PT.PaymentStatusDesc, PT.IsPaid,
E.EntityType as HolderEntityType, E.EntityTypeDesc as HolderEntityTypeDesc, E.PersonTitle, E.PersonFirstname, E.PersonSurname, 
E.OrganisationName, E.FullName as HolderFullName, 
E.MailAddress as HolderMailAddress, E.Suburb as HolderSuburb, E.State as HolderState, E.Postcode as HolderPostcode, 
E.Mobile as HolderMobile, E.Email, E.PFN as HolderPFN
    , lic.VehicleRegNo as VehicleRegNo 
    , payment.PaymentTypeDesc as PaymentTypeDesc
    , S.StatusTypeDesc as StatusTypeDesc
    , Payment.LastPaymentDate as DatetimeStamp
    --  , Payment.FirstPaymentDate 
from
rptPayment P
inner join vwQry_xhsEntity_Base E on P.HolderHistoryID = E.HistoryID
inner join LicenceType LT on P.LicenceTypeID=LT.LicenceTypeID
inner join LicenceFeeType FT on P.FeeTypeID=FT.FeeTypeID
inner join PaymentStatusType PT on P.PaymentStatusID = PT.PaymentStatusID 
inner join licence Lic on P.licenceID = lic.licenceID
inner join vwLicCurrentStatus S on P.LicenceID=S.LicenceID
inner join 
    (SELECT LFP.licenceID
        , FirstPaymentDate = MIN(LP.DatetimeStamp)
        , LastPaymentDate = MAX(LP.DatetimeStamp)
        , PType.PaymentTypeDesc
    FROM licenceFeePayment LFP 
    INNER JOIN licencePayment LP 
        on LFP.paymentID = LP.paymentID
    INNER JOIN paymentType PType 
        on LP.paymentTypeID = PType.paymentTypeID
    GROUP BY lfp.licenceID, PType.PaymentTypeDesc) Payment
    ON P.licenceID = Payment.licenceID

union

select
P.*,
LT.LicenceTypeDesc, FT.FeeTypeDesc, FT.ThroughputText, FT.Prorata as IsProrata, PT.PaymentStatusDesc, PT.IsPaid,
E.EntityType as HolderEntityType, E.EntityTypeDesc as HolderEntityTypeDesc, E.PersonTitle, E.PersonFirstname, E.PersonSurname, 
E.OrganisationName, E.FullName as HolderFullName, 
E.MailAddress as HolderMailAddress, E.Suburb as HolderSuburb, E.State as HolderState, E.Postcode as HolderPostcode, 
E.Mobile as HolderMobile, E.Email, E.PFN as HolderPFN
    , lic.VehicleRegNo as VehicleRegNo 
    , payment.PaymentTypeDesc as PaymentTypeDesc
    , S.StatusTypeDesc as StatusTypeDesc
    , Payment.LastPaymentDate as DatetimeStamp
    --  , Payment.FirstPaymentDate 

from rptPayment P
inner join vwQry_xhsEntity_Base E on P.HolderHistoryID = E.HistoryID
inner join LicenceType LT on P.LicenceTypeID=LT.LicenceTypeID
inner join LicenceFeeType FT on P.FeeTypeID=FT.FeeTypeID
inner join PaymentStatusType PT on P.PaymentStatusID = PT.PaymentStatusID 
inner join licence Lic on P.licenceID = lic.licenceID
inner join vwLicCurrentStatus S on P.LicenceID=S.LicenceID
inner join 
    (SELECT LFP.licenceID
        , FirstPaymentDate = MIN(LP.DatetimeStamp)
        , LastPaymentDate = MAX(LP.DatetimeStamp)
        , PType.PaymentTypeDesc
    FROM licenceFeePayment LFP 
    INNER JOIN licencePayment LP 
        on LFP.paymentID = LP.paymentID
    INNER JOIN paymentType PType 
        on LP.paymentTypeID = PType.paymentTypeID

    GROUP BY lfp.licenceID, PType.PaymentTypeDesc) Payment
    ON P.licenceID = Payment.licenceID

2 个答案:

答案 0 :(得分:1)

首先需要解决的基本业务问题是确定如何向最终用户提供数据。在LastPaymentDate的情况下,您基本上决定最后一个DatetimeStamp是获胜记录。使用最简单的方法选择此记录:子查询中列的最大值。如果您有多种付款类型说明,付款来源或其他付款方式属性,则您需要选择一个赢家或将这些值汇总在一起。这为您提供了几个选项:

  1. 如果您只需要上次付款的属性,则可以使用ROW_NUMBER()聚合窗口函数,然后将您的联接限制为最后一行的数据。对于非常大的数据集,这可能很昂贵,但您可以在事务系统本身或在一般ETL过程中的部分数据集上分配值。如果数据集相对较小,则成本可能微不足道。

  2. 如果每个表中都有可靠的上次修改日期,则可以运行子查询以返回上次修改的最大日期,并过滤到满足此条件的行。如果多行具有相同的最后修改日期,这种方法也可能表现不佳,也可能无法正常工作,那么您仍可能返回多行。

  3. 您可以将所有选定的付款方式,来源或其他属性连接到一个字段中。这通常是一个很差的解决方案,因为用户可能需要分离出的值,但在某些情况下,这是正确的解决方案。您需要与业务所有者一起确定这是否是正确的解决方案。

  4. 您可以与请求此数据的企业主交谈,并说明数据的工作原理以及如何为他们提供数据的可能排列方式。一些用户可能想要最后的支付属性,一些用户可能想要连接数据,一些用户可能想要整个历史记录,这意味着您可能需要提供单独的非聚合支付详细信息报告。我建议在做出假设,编写解决方案以及发现需要重新编码整个解决方案之前与利益相关方进行审核,因为它不符合业务需求。

  5. 以下的原始回复

    如果LicensePayment表包含多个不同的值,则将UNION ALL更改为UNION将是不够的。您需要决定此表是否需要在单独的字段中获取第一个付款日期,最后付款日期或两者。您可以通过以下方式执行此操作:(1)将连接转换为LicensePayment表到子查询,或(2)单独聚合LicensePayment数据并加入聚合表。以下是使用第一种方法的示例查询:

    CREATE view vwQry_LicencePayments 
    as
    
    select
    P.*,
    LT.LicenceTypeDesc, FT.FeeTypeDesc, FT.ThroughputText, FT.Prorata as IsProrata, PT.PaymentStatusDesc, PT.IsPaid,
    E.EntityType as HolderEntityType, E.EntityTypeDesc as HolderEntityTypeDesc, E.PersonTitle, E.PersonFirstname, E.PersonSurname, 
    E.OrganisationName, E.FullName as HolderFullName, 
    E.MailAddress as HolderMailAddress, E.Suburb as HolderSuburb, E.State as HolderState, E.Postcode as HolderPostcode, 
    E.Mobile as HolderMobile, E.Email, E.PFN as HolderPFN
        , Payment.FirstPaymentDate
        , Payment.LastPaymentDate
    from
    rptPayment P
    inner join vwQry_xhsEntity_Base E on P.HolderHistoryID = E.HistoryID
    inner join LicenceType LT on P.LicenceTypeID=LT.LicenceTypeID
    inner join LicenceFeeType FT on P.FeeTypeID=FT.FeeTypeID
    inner join PaymentStatusType PT on P.PaymentStatusID = PT.PaymentStatusID 
    inner join 
        (SELECT LFP.LicenseID
            , FirstPaymentDate = MIN(LP.DatetimeStamp)
            , LastPaymentDate = MAX(LP.DatetimeStamp)
        FROM licenceFeePayment LFP 
        INNER JOIN licencePayment LP 
            on LFP.paymentID = LP.paymentID
        GROUP BY lfp.LicenseID) Payment
        ON P.licenceID = Payment.licenceID
    union ALL
    select
    P.*,
    LT.LicenceTypeDesc, FT.FeeTypeDesc, FT.ThroughputText, FT.Prorata as IsProrata, PT.PaymentStatusDesc, PT.IsPaid,
    E.EntityType as HolderEntityType, E.EntityTypeDesc as HolderEntityTypeDesc, E.PersonTitle, E.PersonFirstname, E.PersonSurname, 
    E.OrganisationName, E.FullName as HolderFullName, 
    E.MailAddress as HolderMailAddress, E.Suburb as HolderSuburb, E.State as HolderState, E.Postcode as HolderPostcode, 
    E.Mobile as HolderMobile, E.Email, E.PFN as HolderPFN
        , Payment.FirstPaymentDate
        , Payment.LastPaymentDate
    from rptPayment P
    inner join vwQry_Entity E on P.HolderID = E.EntityID
    inner join LicenceType LT on P.LicenceTypeID=LT.LicenceTypeID
    inner join LicenceFeeType FT on P.FeeTypeID=FT.FeeTypeID
    inner join PaymentStatusType PT on P.PaymentStatusID = PT.PaymentStatusID
    inner join 
        (SELECT lfp.LicenseID
            , FirstPaymentDate = MIN(LP.DatetimeStamp)
            , LastPaymentDate = MAX(LP.DatetimeStamp)
        FROM licenceFeePayment LFP 
        INNER JOIN licencePayment LP 
            on LFP.paymentID = LP.paymentID
        GROUP BY lfp.LicenseID) Payment
        ON P.licenceID = Payment.licenceID
    WHERE p.HolderHistoryID is null;
    

答案 1 :(得分:0)

正如肯指出,你必须使用UNION,而不是UNION ALL

另见What is the difference between UNION and UNION ALL?