SQL Server:一个查询中存在一行,另一个查询中缺少该行

gor*_*igh 5 sql sql-server-2008

好的,我想我一定是对SQL查询有些误解.这是一个非常罗嗦的问题,所以感谢花时间阅读它(我的问题在最后,其他一切都只是上下文).

我正在编写一个以复式本金为主的会计系统 - 货币总是在账户之间移动,交易是2 TransactionParts行或更多行减少一个账户并递增另一个账户.

某些TransactionParts行可能被标记为与税收相关,因此系统可以生成增值税销售/购买总额的报告等,因此单个交易可能有两个TransactionParts引用相同的帐户 - 一个增值税相关,另一个不相关.为了简化向用户的呈现,我有一个视图,可以为同一个帐户和事务组合多个行:

create view Accounting.CondensedEntryView as
select p.[Transaction], p.Account, sum(p.Amount) as Amount
    from Accounting.TransactionParts p 
    group by p.[Transaction], p.Account
Run Code Online (Sandbox Code Playgroud)

然后,我有一个计算运行平衡列的视图,如下所示:

create view Accounting.TransactionBalanceView as
with cte as
(
    select ROW_NUMBER() over (order by t.[Date]) AS RowNumber, 
                         t.ID as [Transaction], p.Amount, p.Account
        from Accounting.Transactions t
            inner join Accounting.CondensedEntryView p on p.[Transaction]=t.ID
)
select b.RowNumber, b.[Transaction], a.Account, 
                coalesce(sum(a.Amount), 0) as Balance
    from cte a, cte b
    where a.RowNumber <= b.RowNumber AND a.Account=b.Account
    group by b.RowNumber, b.[Transaction], a.Account
Run Code Online (Sandbox Code Playgroud)

由于我还没有解决的原因,某个交易(ID = 30)没有出现在用户的账户对账单上.我通过跑步证实了这一点

select * from Accounting.TransactionBalanceView where [Transaction]=30
Run Code Online (Sandbox Code Playgroud)

这给了我以下结果:

RowNumber            Transaction Account Balance
-------------------- ----------- ------- ---------------------
72                   30          23      143.80
Run Code Online (Sandbox Code Playgroud)

正如我之前所说,TransactionParts每个交易应该至少有两个,所以其中一个没有出现在我看来.我假设我编写视图的方式一定存在问题,并运行查询以查看是否还有其他缺失:

select [Transaction], count(*) 
    from Accounting.TransactionBalanceView 
    group by [Transaction] 
    having count(*) < 2
Run Code Online (Sandbox Code Playgroud)

此查询不返回任何结果 - 甚至不包括事务30!想我必须是个白痴我运行以下查询:

select [Transaction] 
    from Accounting.TransactionBalanceView 
    where [Transaction]=30
Run Code Online (Sandbox Code Playgroud)

它返回两行!因此select *只返回一行并select [Transaction]返回两者.在经历了最后两次查询并且重新运行后,我得出结论,我并不知道发生了什么.有任何想法吗?

非常感谢,如果你这么坚持我!

编辑:

以下是执行计划:

选择*
选择[交易]

每个1000行,因此找到了其他地方托管.

编辑2:

为了完整起见,这是我使用的表格:

create table Accounting.Accounts
(
ID              smallint        identity primary key,
[Name]          varchar(50)     not null
    constraint UQ_AccountName unique,
[Type]          tinyint         not null
    constraint FK_AccountType foreign key references Accounting.AccountTypes
);

create table Accounting.Transactions
(
ID              int             identity primary key,
[Date]          date            not null default getdate(),
[Description]   varchar(50)     not null,
Reference       varchar(20)     not null default '',
Memo            varchar(1000)   not null
);

create table Accounting.TransactionParts
(
ID              int             identity primary key,
[Transaction]   int             not null
    constraint FK_TransactionPart foreign key references Accounting.Transactions,
Account         smallint        not null
    constraint FK_TransactionAccount foreign key references Accounting.Accounts,
Amount          money           not null,
VatRelated      bit             not null default 0
);
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 6

演示可能的解释.

创建表脚本

SELECT *
INTO #T
 FROM master.dbo.spt_values

 CREATE NONCLUSTERED INDEX [IX_T] ON  #T ([name] DESC,[number] DESC);
Run Code Online (Sandbox Code Playgroud)

查询一(返回35结果)

WITH cte AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY NAME) AS rn
 FROM  #T
)
SELECT c1.number,c1.[type] 
FROM cte c1
JOIN cte c2 ON c1.rn=c2.rn AND c1.number <> c2.number
Run Code Online (Sandbox Code Playgroud)

查询二(与之前相同但将c2.[type]添加到选择列表使其返回0结果) ;

WITH cte AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY NAME) AS rn
 FROM  #T
)
SELECT c1.number,c1.[type] ,c2.[type] 
FROM cte c1
JOIN cte c2 ON c1.rn=c2.rn AND c1.number <> c2.number
Run Code Online (Sandbox Code Playgroud)

为什么?

未指定重复NAME的row_number(),因此它只选择适合所需输出列的最佳执行计划的任何一个.在第二个查询中,对于两个cte调用都是相同的,在第一个查询中,它选择不同的访问路径,结果是不同的row_numbering.

执行计划

建议的解决方案

你是自己加入CTE的 ROW_NUMBER() over (order by t.[Date])

与可能预期的相反,CTE可能不会实现,这将确保自连接的一致性,因此您假设ROW_NUMBER()双方之间的相关性可能不存在于数据中[Date]存在重复的记录 .

如果您尝试ROW_NUMBER() over (order by t.[Date], t.[id])确保在绑定日期时row_numbering处于保证一致的顺序,该怎么办?(或者如果id不能执行此操作,可以区分记录的其他一些列/列组合)