为什么这个显然多余的联接会修复我的查询?

时间:2018-04-23 15:41:56

标签: sql-server sql-server-2012

虽然重新构建了一个问题查询,但我偶然发现一个明显多余的连接修复它的事实。我不懂为什么。 (我难以简洁地解释;请耐心等待。业务背景 - 报告及其参数 - 并不重要。)

我有三个表来保存对各种报告的调用历史记录,包括传递给报告的参数。报表参数值始终存储为字符串,如下所示:

CREATE TABLE [dbo].[ImportReportParameter](
[ImportReportParameterID] [int] IDENTITY(1,1) NOT NULL,
[ImportReportHistoryID] [int] NOT NULL,
[ParameterName] [nvarchar](100) NOT NULL,
[ParameterValue] [nvarchar](200) NULL,
…

所有报告都接受日期范围,因此我知道每个ImportReportHistoryID都有一个ImportReportParameter行,其ParamaterName为“RangeStart”,其一个ParameterName为“RangeEnd”。我知道这些行中的ParameterValue字符串可以安全地转换为DateTime。 有争议的查询将数据展平为每个报表实例产生一行,其中包含日期范围作为一对DateTimes:

select top (@numberToRetrieve) ir.*,
    (select cast(ParameterValue as datetime) from ImportReportParameter iparm where iparm.ImportReportHistoryID = ir.ImportReportHistoryID and ParameterName = 'RangeStart') RangeStart,
    (select cast(ParameterValue as datetime) from ImportReportParameter iparm where iparm.ImportReportHistoryID = ir.ImportReportHistoryID and ParameterName = 'RangeEnd') RangeEnd
from  ImportReportHistory ir 
order by Requested desc

这一切都运作良好了一段时间。但随后查询开始失败:

  
    

Msg 241,Level 16,State 1,Line 2

         

从字符串转换日期和/或时间时转换失败。

  

我假设坏数据已进入ParameterValue字段,但确认成功执行并非如此:

select ImportReportHistoryID, cast(ParameterValue as datetime) RangeStart 
from ImportReportParameter 
where ParameterName = 'RangeStart' 

,同样地:

select ImportReportHistoryID, cast(ParameterValue as datetime) RangeEnd 
from ImportReportParameter 
where ParameterName = 'RangeEnd'  

然后我开始改变@numberToRetrieve,事情变得奇怪了。对于最多46的值,查询可以工作,但是我看不到第47个实例有什么问题...我试过只运行第47个实例,它很好。然后我从订单中删除了“desc”,这也是有效的,直到第46个。 但是我的测试ImportReportHistory数据集只有80行。所以我可以使用此查询安全地检索所有记录:isn'其中任何数据都存在问题。我检索哪些并不重要,只要我只检索其中的46个。我认为这很奇怪。

无法弄明白我决定重新配置使用连接到参数表,并意识到只是添加明显多余的连接修复了一些事情:

…
from  ImportReportHistory ir 
    left join ImportReportParameter pSuperfluous on ir.ImportReportHistoryID = pSuperfluous.ImportReportHistoryID

所以我的问题是:为什么多余的联接“修复”一个查询,据我所知,该查询应该有效?

编辑:实际上它并没有修复它,因为我现在每个ImportReportHistoryID获得多行 - 我现在正在排序 - 但它解决了投射问题。

编辑(2):多余的连接并没有真正解决它,它只是让它发生频率降低。我现在重新制定了查询:

select top (@numberToRetrieve) ir.*, t.DisplayName, 
    p1.RangeStart, 
    p2.RangeEnd,
from  ImportReportHistory ir 
    left join ImportReportTemplate t on ir.ImportReportTemplateID = t.ImportReportTemplateID
    left join (select ImportReportHistoryID, cast(ParameterValue as datetime) RangeStart from ImportReportParameter where ParameterName = 'RangeStart') p1 on p1.ImportReportHistoryID = ir.ImportReportHistoryID
    left join (select ImportReportHistoryID, cast(ParameterValue as datetime) RangeEnd from ImportReportParameter where ParameterName = 'RangeEnd') p2 on p2.ImportReportHistoryID = ir.ImportReportHistoryID
order by Requested desc

现在有效,但是:

我原来问题的答案似乎在于Martin在评论中指出的关于SELECT和WHERE处理顺序的讨论。据我所知,select 可以在where之前处理,这就是为什么有时试图将ParameterValue作为DateTime转换为ParameterName不是RangeStart或RangeEnd的行的原因。这会产生不可预测的结果,并且似乎违反了Microsoft记录的逻辑处理顺序。这也意味着我重新制定的查询(以及我过去撰写过的其他查询)有一天会失败,所以它还不是一个令人满意的解决方案。

0 个答案:

没有答案