【问题标题】:How to make SQL comparisons by using values from another table如何使用另一个表中的值进行 SQL 比较
【发布时间】:2021-06-10 07:49:22
【问题描述】:

我有这样的下表:

表 1:

我想通过使用表 2 中的以下范围在表 1 中创建几个段 (ScoreType)。

表 2:

我使用了简单的 case 语句并使用 table-2 中的值进行比较,因为我可以随时更新该表,而不是在查询中手动更改。

制作表格的代码:

--Table 1
Create table BandRange(
    [ScoreType] Varchar(10)
    ,[MinDemand] INT
    ,[MaxDemand] INT
    ,[MinFrequency] INT
    ,[MaxFrequency] INT
    ,[ForWhat] Varchar(10) 
);

Insert into BandRange
values
 ('ND-NF' , 0,0,0,0,'QtrYear')
,('ND-LF' , 0,0,0,5,'QtrYear')
,('ND-MF' , 0,0,6,15,'QtrYear')
,('ND-HF' , 0,0,16,99999,'QtrYear')

,('LD-NF' , 1,2,0,0,'QtrYear')
,('LD-LF' , 1,2,0,5,'QtrYear')
,('LD-MF' , 1,2,6,15,'QtrYear')
,('LD-HF' , 1,2,16,99999,'QtrYear')

,('MD-NF' , 3,5,0,0,'QtrYear')
,('MD-LF' , 3,5,0,5,'QtrYear')
,('MD-MF' , 3,5,6,15,'QtrYear')
,('MD-HF' , 3,5,16,99999,'QtrYear')

,('HD-NF' , 6,99999,0,0,'QtrYear')
,('HD-LF' , 6,99999,0,5,'QtrYear')
,('HD-MF' , 6,99999,6,15,'QtrYear')
,('HD-HF' , 6,99999,16,99999,'QtrYear');

--Table 2
create table QtrScores(MUID INT NOT NULL,Demand INT NOT NULL,Frequency INT NOT NULL);
Insert into QtrScores Values
(1,52,35),
(2,0,5),
(3,5,2),
(4,1,81),
(5,6,5),
(6,4,0),
(7,14,1)

我的代码:

--select * from QtrScores
--select * from BandRange

select MUID,Frequency,Demand,
    case 
        --1st
        when (Demand = (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-NF') 
            or Demand = ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-NF' ) 
            ) and ( 
            Frequency = (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-NF')  
            or  Frequency = (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-NF') 
             ) 
            then 'ND-NF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-LF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-LF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-LF')  
            and Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-LF') 
             ) 
            then 'ND-LF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-MF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-MF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-MF')  
            and  Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-MF') 
             ) 
            then 'ND-MF' 
        when (Demand <= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-HF') 
            and Demand >= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-HF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-HF')  
            and Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='ND-HF') 
             ) 
            then 'ND-HF' 
        
        --2nd
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-NF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-NF' ) 
            ) and ( 
            Frequency = (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-NF')  
            and Frequency = (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-NF') 
             ) 
            then 'LD-NF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-LF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-LF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-LF')  
            and  Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-LF') 
             ) 
            then 'LD-LF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-MF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-MF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-MF')  
            and  Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-MF') 
             ) 
            then 'LD-MF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-HF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-HF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-HF')  
            and  Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='LD-HF') 
             ) 
            then 'LD-HF'

        --3rd
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-NF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-NF' ) 
            ) and ( 
            Frequency = (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-NF')  
            and  Frequency = (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-NF') 
             ) 
            then 'MD-NF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-LF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-LF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-LF')  
            and  Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-LF') 
             ) 
            then 'MD-LF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-MF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-MF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-MF')  
            and  Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-MF') 
             ) 
            then 'MD-MF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-HF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-HF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-HF')  
            and Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='MD-HF') 
             ) 
            then 'MD-HF'
        
        --4th
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-NF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-NF' ) 
            ) and ( 
            Frequency = (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-NF')  
            and  Frequency = (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-NF') 
             ) 
            then 'HD-NF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-LF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-LF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-LF')  
            and  Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-LF') 
             ) 
            then 'HD-LF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-MF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-MF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-MF')  
            and  Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-MF') 
             ) 
            then 'HD-MF' 
        when (Demand >= (select MinDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-HF') 
            and Demand <= ( select MaxDemand from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-HF' ) 
            ) and ( 
            Frequency >= (select MinFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-HF')  
            and  Frequency <= (select MaxFrequency from  BandRange(nolock) where ForWhat = 'QtrYear' and ScoreType ='HD-HF') 
             ) 
            then 'HD-HF'
            else 'NA'
        End  as ScoreType
    from QtrScores(nolock)  

我在 QtrYear 的表 1 中有大约 2500 万条记录,执行大约需要 1 小时 30 分钟。我对各种 QtrYear、HalfYear、Yearwise 有多个条件,这需要 10 多个小时才能执行。有更好的方法吗?

SQL Fiddle 的链接: http://sqlfiddle.com/#!18/60c62/5

【问题讨论】:

  • 摆脱所有这些子查询将是朝着修复性能迈出的一大步。摆脱NOLOCK 提示是另一回事,对于如此长时间运行的查询,我不怀疑你最终会得到不正确的结果(你知道NOLOCK does that, right?). Note, as well, these are CASE` 表达式,而不是语句. T-SQL 不支持Case (Switch) 语句,这些语句会改变操作的逻辑流程,只支持CASE 表达式。
  • 您可能需要考虑为您的示例数据包含所需的输出。
  • 你想要什么结果?问题不清楚。

标签: sql sql-server-2012 case query-optimization


【解决方案1】:

这里的挑战是您要针对 BandRange 表进行 2500 万条记录乘以 4 次查询 - 5000 万次选择查询 - 即使是最大的服务器也足以阻塞 -

换种方式思考 - 让数据为您工作 - 在加载 QtrScores 表时为相关分带准备索引 -

额外的好处 - 如果您将来需要添加新条件 - 您将不需要维护 case 语句。简单地添加或更改 BandRanges 记录集将很容易,并且更新 QtrScores 将是一个不需要更改的夜间加载过程 - 请注意 BandRange_ID 是合成的,不用于参考。

第 1 步 - 在 BandRange 表上应用数字标识键

Step1a - 部署计数表

步骤 1b - 将数字键绑定到 BandRanges -

第 2 步 - 准备在 QtrScores 表中维护一个新列

第 2a 步 - 部署一个标量函数,使 QtrScores 新列保持最新

第 2b 步 - 将整数列添加到 QtrScores - 和非聚集索引以获得良好的衡量标准

第 3 步 - 使用上述函数(定期)更新 QtrScores 表中的新列

第 4 步 - 带有新的简单查询的最终输出

现在了解详情

第 1 步 - 在 BandRange 表上应用数字标识键 - 就个人而言 - 如果您不想更改 BandRange 表 - 使用一个名为 tally 的新表 **** 步骤 1a - 部署 Tally 表 **** https://www.sqlservercentral.com/articles/the-numbers-or-tally-table-what-it-is-and-how-it-replaces-a-loop-1

--=============================================================================
--      Setup Tally Table
--=============================================================================
    USE TempDB     --DB that everyone has where we can cause no harm
    SET NOCOUNT ON --Supress the auto-display of rowcounts for appearance/speed
DECLARE @StartTime DATETIME    --Timer to measure total duration
    SET @StartTime = GETDATE() --Start the timer
--=============================================================================
--      Create and populate a Tally table
--=============================================================================
--===== Conditionally drop and create the table/Primary Key
     IF OBJECT_ID('dbo.Tally') IS NOT NULL 
        DROP TABLE dbo.Tally
 CREATE TABLE dbo.Tally 
        (N INT, 
         CONSTRAINT PK_Tally_N PRIMARY KEY CLUSTERED (N))
--===== Create and preset a loop counter
DECLARE @Counter INT
    SET @Counter = 1
--===== Populate the table using the loop and couner
  WHILE @Counter <= 11000
  BEGIN
         INSERT INTO dbo.Tally
                (N)
         VALUES (@Counter)
            SET @Counter = @Counter + 1
    END
--===== Display the total duration
 SELECT STR(DATEDIFF(ms,@StartTime,GETDATE())) + ' Milliseconds duration'

步骤 1b - 将数字键绑定到 BandRanges - 使用计数表(可能有比“Top”更干净的路由器 - 但这里是为了方便

create function [dbo].[fntab_BandRange_Indexed] () returns table return
-- select * from [dbo].[fntab_BandRange_Indexed] () 
select Top (select brrows = count(1) from BandRange) 
[BandRange_ID] = cast(ROW_NUMBER() OVER (ORDER BY t.N) as integer)
, BandRange.* from BandRange
cross apply tempdb.dbo.tally t
GO

使用此查询进行测试(调整 where 和 and)

select BandRange_ID = min([BandRange_ID])
from fntab_BandRange_Indexed ()
where 0 between MinDemand and MaxDemand 
and 0 Between MinFrequency and MaxFrequency

第 2 步 - 准备在 QtrScores 表中维护一个新列

第 2a 步 - 部署标量函数 用于定期更新 QtrScores 表(新列尚未创建)

create function sclr_BandRange_Indexed_Sel ( @Demand integer, @Frequency integer) 
returns integer as
begin
declare @bandrange_id integer
select @BandRange_ID = cast(min([BandRange_ID]) as integer)
from fntab_BandRange_Indexed ()
where @Demand between MinDemand and MaxDemand 
and @Frequency Between MinFrequency and MaxFrequency
return @BandRange_ID 
end

第 2b 步 - 将整数列添加到 QtrScores - 和非聚集索引以获得良好的衡量标准 (本质上将成为 fntab_BandRange_Indexed.BandRange_ID 列的 [unenforced] FK

alter table QtrScores add [BandRange_ID] integer;
create nonclustered index idx_QtrScores_BandRangeID_MUID_Demand_Frequency on QtrScores (BandRange_ID, MUID, Demand, Frequency);

第 3 步 - 使用上述函数(定期)更新 QtrScores 表中的新列 每次附加 QtrScores 表时,在您的计划中应用 BandRange_ID 的更新(可以在插入时使用触发器 - 但这是为了说明)

update QtrScores  
   set [BandRange_ID] = dbo.sclr_BandRange_Indexed_Sel ( Demand, Frequency )

第 4 步 - 带有新的简单查询的最终输出 而不是庞大而笨拙的案例陈述 - 您现在可以重新创建您的预期结果 使用简单的内部连接

select QtrScores.MUID, QtrScores.Frequency, QtrScores.Demand, brind.ScoreType
from QtrScores
inner join [fntab_BandRange_Indexed]() brind on brind.BandRange_ID = QtrScores.BandRange_ID
 -- where [ForWhat] in ('QtrYear')
order by MUID

注意 - 由于“ForWhat”是 case 语句中的一个常量,我没有添加它 - 但使用最终的 SQL 语句很容易(第 4 步)

【讨论】:

    【解决方案2】:

    您想要做的最合乎逻辑的解释就是join

    select *
    from QtrScores q join
         BandRange b
         on q.demand between b.mindemand and b.maxdemand and
            q.frequency between b.minfrequency and b.maxfrequency;
    

    Here 是一个 SQLFiddle。

    【讨论】:

      猜你喜欢
      • 2021-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-21
      • 2020-09-30
      • 1970-01-01
      相关资源
      最近更新 更多