SQL返回具有最高索引的多个非空值

SQL返回具有最高索引的多个非空值,sql,sql-server,sql-server-2008-r2,Sql,Sql Server,Sql Server 2008 R2,SQL Server 2008 R2数据库中有以下表格: 客户: CustID CustName ====== ======== 1 A 2 B 3 C 4 D TransID CustID InvoiceTotal LoyaltyPointsEarned ======= ====== ============ =================== 1 1 300 25 2

SQL Server 2008 R2数据库中有以下表格:

客户

CustID  CustName
======  ========
1       A
2       B
3       C
4       D
TransID  CustID  InvoiceTotal  LoyaltyPointsEarned
=======  ======  ============  ===================
1        1       300           25
2        2       NULL          10
3        3       100           10
4        2       200           25
5        1       NULL          100
6        3       120           NULL
交易

CustID  CustName
======  ========
1       A
2       B
3       C
4       D
TransID  CustID  InvoiceTotal  LoyaltyPointsEarned
=======  ======  ============  ===================
1        1       300           25
2        2       NULL          10
3        3       100           10
4        2       200           25
5        1       NULL          100
6        3       120           NULL
事务按时间顺序插入(ID越高=最近的顺序);事务允许InvoiceTotal或LoyaltyPointsEarned为空,但不能同时为空

我想获得所有客户最近获得的非空发票总额和(这是一个棘手的问题)最近获得的非空忠诚度点数,每个客户的这一信息显示在同一行上:

CustID  CustName  LatestInvoiceTotal  LatestLoyaltyPointsEarned
1       A         300                 100
2       B         200                 25
3       C         120                 10
以下查询提供了最新的发票总额:

SELECT DISTINCT 
    CustID, CustName, LatestInvoiceTotal, LatestLoyaltyPointsEarned
FROM 
    Customers
INNER JOIN 
    (SELECT 
         CustID, InvoiceTotal AS LatestInvoiceTotal, TransID 
     FROM 
         Transactions 
     GROUP BY 
         CustID, InvoiceTotal, TransID) CustomerTransactions ON Customers.CustID = CustomerTransactions.CustID
INNER JOIN 
    (SELECT 
         CustID, MAX(TransID) AS MaxTransID 
     FROM 
         Transactions 
     WHERE 
         InvoiceTotal IS NOT NULL 
     GROUP BY 
         CustID) MaxTransactionIDs ON Customers.CustID = MaxTransactionIDs.CustID AND CustomerTransactions.TransID = MaxTransactionIDs.MaxTransID

如何在不复制结果中的客户记录的情况下,将其扩展为对忠诚度Intsearned执行相同操作?

您可以使用窗口功能

不过,您需要2个子查询:

with tmp as (
 select custId, 
  InvoiceTotal, 
  row_number() over(partition by custId order by TransId desc) as rnk
 from Transactions
 where InvoiceTotal is not null)
select *
from tmp
where rnk = 1

你可以使用窗户功能

不过,您需要2个子查询:

with tmp as (
 select custId, 
  InvoiceTotal, 
  row_number() over(partition by custId order by TransId desc) as rnk
 from Transactions
 where InvoiceTotal is not null)
select *
from tmp
where rnk = 1

简单的解决方案是使用两个子查询来检索该信息

select CustID, CustName, 
       (select top 1 InvoiceTotal
        from Transactions
        where Transactions.CustID = Customers.CustID and InvoiceTotal is not null
        order by TransID desc) as LatestInvoiceTotal,
       (select top 1 LoyaltyPointsEarned
        from Transactions
        where Transactions.CustID = Customers.CustID and LoyaltyPointsEarnedis not null
        order by TransID desc) as LatestLoyaltyPointsEarned               
from Customers

但是,由于子查询可能会严重降低性能,您只需确保在CustID上的事务上有一个多索引,TransID降序,这样这些子查询就会得到优化。

简单的解决方案是使用两个子查询检索该信息

select CustID, CustName, 
       (select top 1 InvoiceTotal
        from Transactions
        where Transactions.CustID = Customers.CustID and InvoiceTotal is not null
        order by TransID desc) as LatestInvoiceTotal,
       (select top 1 LoyaltyPointsEarned
        from Transactions
        where Transactions.CustID = Customers.CustID and LoyaltyPointsEarnedis not null
        order by TransID desc) as LatestLoyaltyPointsEarned               
from Customers

但是,由于子查询可能会严重降低您的性能,您只需确保在CustID上的事务上有一个多索引,TransID降序,这样子查询就会得到优化。

另一对连接似乎可以做到这一点:

SELECT DISTINCT 
    CustID, CustName, LatestInvoiceTotal, LatestLoyaltyPointsEarned
FROM 
    Customers
INNER JOIN 
    (SELECT 
         CustID, InvoiceTotal AS LatestInvoiceTotal, TransID 
     FROM 
         Transactions 
     GROUP BY 
         CustID, InvoiceTotal, TransID) CustomerTransactions ON Customers.CustID = CustomerTransactions.CustID
INNER JOIN 
    (SELECT 
         CustID, MAX(TransID) AS MaxTransID 
     FROM 
         Transactions 
     WHERE 
         InvoiceTotal IS NOT NULL 
     GROUP BY 
         CustID) MaxTransactionIDs ON Customers.CustID = MaxTransactionIDs.CustID AND CustomerTransactions.TransID = MaxTransactionIDs.MaxTransID
INNER JOIN 
    (SELECT 
         CustID, LoyaltyPointsEarned AS LatestLoyaltyPointsEarned, TransID 
     FROM 
         Transactions 
     GROUP BY 
         CustID, LoyaltyPointsEarned, TransID) CustomerTransactions2 ON Customers.CustID = CustomerTransactions2.CustID
INNER JOIN 
    (SELECT 
         CustID, MAX(TransID) AS MaxTransID 
     FROM 
         Transactions 
     WHERE 
         LoyaltyPointsEarned IS NOT NULL 
     GROUP BY 
         CustID) MaxTransactionIDs2 ON Customers.CustID = MaxTransactionIDs2.CustID AND CustomerTransactions2.TransID = MaxTransactionIDs2.MaxTransID

另一对连接似乎可以做到这一点:

SELECT DISTINCT 
    CustID, CustName, LatestInvoiceTotal, LatestLoyaltyPointsEarned
FROM 
    Customers
INNER JOIN 
    (SELECT 
         CustID, InvoiceTotal AS LatestInvoiceTotal, TransID 
     FROM 
         Transactions 
     GROUP BY 
         CustID, InvoiceTotal, TransID) CustomerTransactions ON Customers.CustID = CustomerTransactions.CustID
INNER JOIN 
    (SELECT 
         CustID, MAX(TransID) AS MaxTransID 
     FROM 
         Transactions 
     WHERE 
         InvoiceTotal IS NOT NULL 
     GROUP BY 
         CustID) MaxTransactionIDs ON Customers.CustID = MaxTransactionIDs.CustID AND CustomerTransactions.TransID = MaxTransactionIDs.MaxTransID
INNER JOIN 
    (SELECT 
         CustID, LoyaltyPointsEarned AS LatestLoyaltyPointsEarned, TransID 
     FROM 
         Transactions 
     GROUP BY 
         CustID, LoyaltyPointsEarned, TransID) CustomerTransactions2 ON Customers.CustID = CustomerTransactions2.CustID
INNER JOIN 
    (SELECT 
         CustID, MAX(TransID) AS MaxTransID 
     FROM 
         Transactions 
     WHERE 
         LoyaltyPointsEarned IS NOT NULL 
     GROUP BY 
         CustID) MaxTransactionIDs2 ON Customers.CustID = MaxTransactionIDs2.CustID AND CustomerTransactions2.TransID = MaxTransactionIDs2.MaxTransID

使用下面的查询获得所需的结果。。MAX()OVER()函数可以在这种情况下使用

;WITH cte_1
AS
 (SELECT a.CustID  ,a.CustName,MAX(InvoiceTotal ) OVER( Partition by a.CustID  Order by TransID  desc )LatestInvoiceTotal  
        ,MAX(LoyaltyPointsEarned ) OVER( Partition by a.CustID  Order by TransID  desc )LatestLoyaltyPointsEarned
        FROM Customers a
            JOIN Transactions b
                ON  a.CustID  =b.CustID )
 SELECT DISTINCT *
 FROM cte_1
 WHERE LatestInvoiceTotal IS NOT NULL AND LatestLoyaltyPointsEarned is NOT NULL
以下是测试场景:

DROP TABLE #Customers

DROP TABLE #Transactions


 CREATE TABLE #Customers
 (
 CustId INT,
 CustName VARCHAR(50)
 )

  CREATE TABLE #Transactions
 (
 TransID   INT,
 CustID   INT,
 InvoiceTotal  INT,
 LoyaltyPointsEarned INT
 )

 INSERT INTO #Customers
 VALUES (1,'A'),(2,'B'),(3,'C'),(4,'D')

 INSERT INTO #Transactions
 VALUES (1,1,300,25),(2,2,NULL,10),(3,3,100,10),(4,2,200,25),(5,1,NULL,100),(6,3,120,NULL)


;WITH cte_1
AS
 (SELECT a.CustID  ,a.CustName,MAX(InvoiceTotal ) OVER( Partition by a.CustID  Order by TransID  desc )LatestInvoiceTotal  
        ,MAX(LoyaltyPointsEarned ) OVER( Partition by a.CustID  Order by TransID  desc )LatestLoyaltyPointsEarned
        FROM #Customers a
            JOIN #Transactions b
                ON  a.CustID  =b.CustID )
 SELECT DISTINCT *
 FROM cte_1
 WHERE LatestInvoiceTotal IS NOT NULL AND LatestLoyaltyPointsEarned is NOT NULL
输出:


使用以下查询获得所需结果。。MAX()OVER()函数可以在这种情况下使用

;WITH cte_1
AS
 (SELECT a.CustID  ,a.CustName,MAX(InvoiceTotal ) OVER( Partition by a.CustID  Order by TransID  desc )LatestInvoiceTotal  
        ,MAX(LoyaltyPointsEarned ) OVER( Partition by a.CustID  Order by TransID  desc )LatestLoyaltyPointsEarned
        FROM Customers a
            JOIN Transactions b
                ON  a.CustID  =b.CustID )
 SELECT DISTINCT *
 FROM cte_1
 WHERE LatestInvoiceTotal IS NOT NULL AND LatestLoyaltyPointsEarned is NOT NULL
以下是测试场景:

DROP TABLE #Customers

DROP TABLE #Transactions


 CREATE TABLE #Customers
 (
 CustId INT,
 CustName VARCHAR(50)
 )

  CREATE TABLE #Transactions
 (
 TransID   INT,
 CustID   INT,
 InvoiceTotal  INT,
 LoyaltyPointsEarned INT
 )

 INSERT INTO #Customers
 VALUES (1,'A'),(2,'B'),(3,'C'),(4,'D')

 INSERT INTO #Transactions
 VALUES (1,1,300,25),(2,2,NULL,10),(3,3,100,10),(4,2,200,25),(5,1,NULL,100),(6,3,120,NULL)


;WITH cte_1
AS
 (SELECT a.CustID  ,a.CustName,MAX(InvoiceTotal ) OVER( Partition by a.CustID  Order by TransID  desc )LatestInvoiceTotal  
        ,MAX(LoyaltyPointsEarned ) OVER( Partition by a.CustID  Order by TransID  desc )LatestLoyaltyPointsEarned
        FROM #Customers a
            JOIN #Transactions b
                ON  a.CustID  =b.CustID )
 SELECT DISTINCT *
 FROM cte_1
 WHERE LatestInvoiceTotal IS NOT NULL AND LatestLoyaltyPointsEarned is NOT NULL
输出:


这仅返回同时具有至少一个发票值和一个忠诚度积分值的客户。使用上面marc的答案也可以得到空值。这只会返回同时具有至少一个发票值和一个忠诚度积分值的客户。使用上面marc的答案也可以得到空值。这是一种享受,谢谢。我不得不用desc(SQLServer2008R2)替换降序。修复:-)。我仍然习惯于SQL Server的特殊性。这是一种享受,谢谢。我不得不用desc(SQLServer2008R2)替换降序。修复:-)。我仍然习惯于SQL Server的特殊性。