【问题标题】:SQL Dynamic partition in count function计数函数中的 SQL 动态分区
【发布时间】:2017-08-29 12:31:04
【问题描述】:

我有一个与报告服务一起使用的存储过程,并且该报告有一些将值传递给存储过程的用户过滤器。由于这些参数,显示的计数会发生变化,所以我想在分区中使用它们来更改计数。我尝试了以下不起作用。

编辑:

计数是在 ss_number 上完成的,而不是在 @SearchBy 上完成的。所以如果我有

SS#       |    Name    |    City  |  Amount

123456789 | Mike Smith | Trenton | 100.00

123456789 | Mike Smith | Trenton | 200.00

123456789 | Mike Smith | Jackson | 100.00

我的计数是 3,即使 @SearchBy = City 并且我在特伦顿进行过滤。

CASE WHEN @SearchBy = 'Product Name' THEN count(ss_number) OVER (PARTITION BY ss_number, @SearchBy) 

现在我正在使用 case 语句,但它大大减慢了我的查询速度。

这是尝试在我的分区中使用变量的没有 case 语句的代码。

CREATE PROCEDURE [dbo].[sp_My_sp]
@SearchBy VARCHAR(MAX), 
@SearchString VARCHAR(MAX),
@SearchNum Int,
@ClaimDate Datetime2

AS
WITH MyCTE AS
(   
SELECT val.Claim_date
, val.Claim_Status
, val.Status_Desc
, ES_Claim_Status
, val.ss_number
, val.name_field1
, val.street_add1
, val.street_add2
, val.city
, val.state
, val.zip_code_pre
, val.reference_number
, val.Game_Name 
, val.val_agent
, val.home_number
, val.work_phone
, val.county_desc
, o.agent_num
, count(ss_number) OVER (PARTITION BY ss_number, @SearchBy) as count
, prize_amount
FROM Sec_Claims val left outer join vw_owners_concat_agent_num o
ON val.SS_NUMBER = convert(varchar(15),o.SS_NO)
where convert(numeric,prize_amount) >= 600)
SELECT Claim_date
, CASE WHEN agent_num IS NULL THEN 'NO' ELSE 'YES' END as "IsRetailer"
, Claim_Status
, Status_Desc
, ES_Claim_Status
, ss_number
, name_field1
, street_add1
, street_add2
, city
, state
, zip_code_pre
, reference_number
, Game_Name 
, val_agent
, home_number
, work_phone
, county_desc
, agent_num
, count
, ROW_NUMBER() OVER(PARTITION BY Name_Field1 ORDER BY Name_Field1) As RowNumber
, convert(decimal(10,2),prize_amount) as prize_amount
, sum(Convert(decimal(9,2),prize_amount)) OVER (PARTITION BY ss_number, Name_Field1) AS prizesum
FROM MyCTE 
WHERE 
    (CASE
        WHEN @SearchBy = 'Agent Number' THEN agent_num
        WHEN @SearchBy = 'SS#' THEN SS_NUMBER
        WHEN @SearchBy = 'Name' THEN Name_Field1
        WHEN @SearchBy = 'Address' THEN STREET_ADD1
        WHEN @SearchBy = 'City' THEN City
        WHEN @SearchBy = 'Claim#' THEN convert(varchar(max),REFERENCE_NUMBER)
        WHEN @SearchBy = 'Validating Retailer' THEN convert(varchar(max),VAL_AGENT)
        WHEN @SearchBy = 'County' THEN COUNTY_DESC
        WHEN @SearchBy = 'Home Phone' THEN convert(varchar(max),HOME_NUMBER)
        WHEN @SearchBy = 'Work Phone' THEN convert(varchar(max),WORK_PHONE)
        WHEN @SearchBy = 'Game Name' THEN GAME_NAME
    END 
    like (@SearchString))
    and
    count>= @SearchNum 
    and
    claim_date > @ClaimDate 
ORDER BY ss_number

【问题讨论】:

  • 您还没有说明为什么您的查询不起作用,因此示例数据和预期输出在这里会很有帮助。您当然不想对静态值进行分区...您想使用列名。
  • 我已经编辑了帖子。对格式感到抱歉,但我想你明白了。
  • 您如何在特伦顿进行过滤? @SearchBy 应该是列名吗?
  • 是 - @SearchBy 是一列
  • Trenton 是否存储了另一个参数?

标签: tsql sql-server-2014 sqlreportingservice


【解决方案1】:


您的查询速度变慢了,因为您可以想象,需要在 Select 语句中对每一行评估“case”语句。
这意味着,如果您的查询加载了许多行,则会产生大量开销。

我看到两种不同的方法来解决您的问题。我不喜欢它们中的任何一个,但它们很有效:)

第一个解决方案
使用 IF ... ELSE 覆盖所有可能的输入。 这意味着您必须多次编写相同的查询才能在一行代码中区分它。
这意味着代码变得冗余并且基本上不可维护

第二个解决方案
构建动态查询。
你可以这样写

DECLARE @myQuery nvarchar(max)
SET @myQuery = 'Select '
if @SearchBy = 'Product Name'
  SET @myQuery= @myQuery + 'count(ss_number) OVER (PARTITION BY ss_number, @SearchBy)'
ELSE
  .... --put here your second case
END

SET @myQuery= @myQuery + 'FROM ... WHERE ...'
EXECUTE spexecute_sql @myQuery

【讨论】:

    【解决方案2】:

    尝试在 Over 语句中使用“Case”:

    Declare @SearchBy varchar(255)
    SET @SearchBy = 'Product Name'
    
    Select count(ss_number)
                OVER (PARTITION BY ss_number,
                                   Case @SearchBy
                                        When 'Product Name' then [Name]
                                        When 'City Name'    then [City]
                                        When 'Product Name' then cast([Amount] as varchar(255))
                                   END) t     
        from (VALUES (123456789,'Mike Smith','Trenton',100.00)
                    ,(123456789,'Mike Smith','Trenton',200.00)
                    ,(123456789,'Mike Smith','Jackson',100.00)
             ) as t([ss_number],Name,City,Amount)
    

    【讨论】:

    • 我会试试的。将 CASE 放在 OVER 中会比我目前拥有的 CASE WHEN @SearchBy = 'City' THEN count(ss_Fid_number) OVER (PARTITION BY ss_fid_number, City) 提高性能吗?
    • 在计数中添加额外的“Case” -> count(CASE @SearchBy when 'City' THEN ss_Fid_number when 'Product Name' then ss_number else NULL end) over(...)
    【解决方案3】:

    根据您的需求,您首先需要动态 SQL。话虽如此,您可以使用row_number()。我仍然不明白您的额外分区,但您可以将其添加到下面的示例中。你可以运行它,它会打印出要执行的命令。

    --create procedure yourProc(
    --                          @SearchBY varchar(64)           --Column to search / filter by
    --                          ,@SearchString varchar(256)     --Values to limit column above by
    --                          ,@Count int                     --limit the count per each SSN
    --                          ,@Date datetime)                
    --as
    
    --begin
    
    declare     @SearchBY varchar(64)       = 'City'            --Column to search / filter by
    declare     @SearchString varchar(256)  = 'Trenton'         --Values to limit column above by
    declare     @Count int                  = 3                 --limit the count per each SSN
    declare     @Date datetime              = '12/25/2017'
    
    declare @SQL nvarchar(max) = 
    
    'with cte as(
        select
            ss_number
            ,Name
            ,City
            ,Amount
            ,row_number() over (partition by ss_number order by (select 1)) as RN
        from
            YourTable
        where
            SomeDateColumn >= ''' + convert(varchar(10),@Date,112) +
            ''' and ' + @SearchBY + ' = ''' + @SearchString +
            ''')
     select *
     from cte
     where ss_number in 
     (select distinct ss_number from cte where RN >= ' + cast(@Count as varchar(16)) + ')
     '
    
    print(@SQL)
    --exec(@SQL)
    --end
    

    这是一个工作示例:http://rextester.com/BPWOXH7777

    【讨论】:

    • 这个解决方案的问题是它只会返回行号大于计数的记录。如果我在报告中选择 5 的计数,但有人有 40 条记录,我需要它返回全部 40 条,而不仅仅是行号 >= 5 的那些。当您说“额外分区”时,我不确定您的意思。我已经用我的 sp 更新了我原来的帖子。
    • 它将返回所有 40 个@Scottdg。如您所见,该子句是 ss_number in (select ... where RN >= @count)。这意味着,如果 ss_number 是 RN >= 5 的组的一部分,则将返回该 ss_number 的所有记录。运行一下看看....
    • 谢谢 - 我读错了。但这也不起作用。我有一个有 6 项索赔的人。其中三个用于同一场比赛。如果我按游戏名称过滤并输入该游戏的值和搜索编号 = 5,我应该只返回那些对该游戏有 5 个或更多声明的人。对于那些可能对该游戏只有一个声明但数据集中至少有 5 个声明的人,我仍然可以得到结果。
    • @Scottdg 你的查询一定是错误的。我发布的那个,最初的 CTE 会根据你传入的列名和值来限制结果。然后,对于这个结果集,它将应用一个 row_number。然后,最终的选择只会拉取>=你传入的数字的ss_number。查询是正确的。您可以发布您正在尝试的内容吗?
    • 我在原始帖子中添加了原始查询。
    猜你喜欢
    • 2012-06-16
    • 1970-01-01
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-07
    • 2016-03-27
    相关资源
    最近更新 更多