将脏SQL代码转换为干净且高效的东西

时间:2013-07-19 16:14:52

标签: sql sql-server-2008 tsql

我必须修改一些似乎没有按预期工作的SQL代码。

SQL代码看起来很糟糕,但它在大多数情况下都有效。

假设我们有多个名称相似的供应商:Microsoft,Microsoft Corp和Microsoft,Inc等。

所有查询返回的都是Microsoft,即使现有代码包含行PRI_VENDOR_NAME like '%' @PRI_VENDOR_NAME '%'(或者,至少它看起来像是这样)。

我似乎无法检查代码是否正常工作,因为它是一个很大的,令人讨厌的代码片段,它将数据附加到一个长字符串中来执行。

当前程序:(准备好尖叫)

ALTER PROCEDURE [dbo].[GetSignalMasterByFilter]
(
@planner varchar(50),
@reorder int,
@release int,
@CMTTED varchar(50),
@partid varchar(50),
@global_short_dt int,
@PRI_VENDOR_NAME varchar(50)
)
AS
BEGIN

DECLARE @Filter nvarchar(4000)
set @Filter = ' '

if @planner <> ''
begin
set @Filter = ' and planner in('  +  @planner + ')'
end

if @reorder = 1
begin
set @Filter = rtrim(@Filter) +  ' and (REORDER_50 = ' +  char(39) + 'Y' + char(39) +  ' )  '
end

if @reorder = 2
begin
set @Filter = rtrim(@Filter) +  ' and (REORDER_30 = ' +  char(39) + 'Y' + char(39) +  ' )  '
end

if @reorder = 3
begin
set @Filter = rtrim(@Filter) +  ' and (REORDER_POINT = ' +  char(39) + 'Y' + char(39) +  ' )  '
end

--if @noaction = 1
--begin
--set @Filter = rtrim(@Filter) +  ' and reorder in (' +  char(39) + 'Excess'  + char(39) + ',' + char(39) + 'Watch'  + char(39)  + ')'
--end

if @release = 1
begin
set @Filter = rtrim(@Filter) +  ' and (RELEASE_50 = ' +  char(39) + 'Y' + char(39) +  ' )  '
end

if @release = 2
begin
set @Filter = rtrim(@Filter) +  ' and (RELEASE_30 = ' +  char(39) + 'Y' + char(39) +  ' )  '
end

if @release = 3
begin
set @Filter = rtrim(@Filter) +  ' and (RELEASE_POINT = ' +  char(39) + 'Y' + char(39) +  ' )  '
end

if @CMTTED <> 'View ALL'
begin
set @Filter = rtrim(@Filter) +  ' and CMTTED > ' +  char(39) + '0'  + char(39)  + ' and isnumeric(CMTTED) = 1 '
end

if @global_short_dt = 1
begin
set @Filter = rtrim(@Filter) +  ' and  (global_short_dt is not null or cast(CMTTED as int) >  cast(ON_HAND as int)) ' 
end

if @global_short_dt = 2 
begin
set @Filter = rtrim(@Filter) +  ' and  (global_short_dt is not null or cast(CMTTED as int) >  cast(ON_HAND as int)) AND ((cast(QTY_IN_STATUS as float) + cast(ON_ORDER as float) + cast(ON_HAND as float)) < cast(CMTTED as int)) ' 
end

if @partid <> ''
begin
set @Filter = rtrim(@Filter) +  ' and partid like(' +  char(39) + @partid + '%' +  char(39) + ')'
end

if @PRI_VENDOR_NAME <> ''
begin
set @Filter = rtrim(@Filter) +  ' and PRI_VENDOR_NAME like(' +  char(39) + @PRI_VENDOR_NAME + '%' +  char(39) + ')'
end



DECLARE @sql nvarchar(4000)
SET @sql  = '
SELECT DISTINCT PRIMARY_VENDOR,case when PRI_VENDOR_NAME is null then PRIMARY_VENDOR else  PRIMARY_VENDOR +'   +  char(39) +    ' - '  +  char(39) +  '+ PRI_VENDOR_NAME end as PRI_VENDOR_NAME 
FROM SignalReportView WHERE PRIMARY_VENDOR is not null '  + rtrim(@filter)  + ' order by PRI_VENDOR_NAME'
--print @sql
EXEC sp_executesql @sql

end

我想要做的是用下面我开始的东西替换那个令人讨厌的字符串变量,但SQL不是我的力量所以它还没有完全返回任何数据:

我的程序版本:不会返回数据,但将来会更清晰,更易于维护。

ALTER PROCEDURE GetSignalMasterByFilter2(
  @planner varchar(50),
  @reorder int,
  @release int,
  @CMTTED varchar(50),
  @partid varchar(50),
  @global_short_dt int,
  @PRI_VENDOR_NAME varchar(50)
) as begin

  SELECT DISTINCT
    PRIMARY_VENDOR,
    case when PRI_VENDOR_NAME is null then PRIMARY_VENDOR else PRIMARY_VENDOR +' - '+ PRI_VENDOR_NAME end as PRI_VENDOR_NAME 
  FROM
    SignalReportView
  WHERE
    (PRIMARY_VENDOR is not null)
    and (
      ISNULL(@planner,0)=0 or
      planner in (@planner))
    and (
      (@reorder=1 and REORDER_50='Y') or
      (@reorder=2 and REORDER_30='Y') or 
      (@reorder=3 and REORDER_POINT='Y') or 
      (1=1)
    )
    and (
      (@release=1 and RELEASE_50='Y') or
      (@release=2 and RELEASE_30='Y') or
      (@release=3 and RELEASE_POINT='Y') or
      (1=1)
    )
    and (
      (@CMTTED='View ALL') or
      (0<CMTTED and ISNUMERIC(CMTTED)=1)
    )
    and (
      (
        (@global_short_dt=1) and
        (
          (GLOBAL_SHORT_DT is not null) or
          (CAST(ON_HAND as int) < CAST(CMTTED as int))
        )
      ) or
      (1=1)
    )
    and (
      (
        (@global_short_dt=2) and
        (
          (GLOBAL_SHORT_DT is not null) or
          (
            (CAST(ON_HAND as int) < CAST(CMTTED as int)) and 
            ((CAST(QTY_IN_STATUS as float) + CAST(ON_ORDER as float) + CAST(ON_HAND as float)) < CAST(CMTTED as int))
          )
        )
      ) or
      (1=1)
    )
    and (
      ISNULL(@partid,0)=0 or
      (PARTID like '%'+@partid+'%')
    )
    and (
      ISNULL(@PRI_VENDOR_NAME,0)=0 or
      (PRI_VENDOR_NAME like '%'+@PRI_VENDOR_NAME+'%')
    )
  ORDER BY PRI_VENDOR_NAME

end

所以,我的问题是:

用其他开发人员将来更容易维护的版本重写原始脚本是个好主意吗?

如果,是否有人能够发现现有SQL未返回所有供应商的原因?

如果,有人可以指导我设计我的版本吗?它目前不工作 - 可能是因为我有一些逻辑错误。此外,(1=1)条款与我的关系并不好,但我不知道如何绕过它们。由于我的程序没有返回任何数据,我现在无法使用它。

我为没有发布表格结构而道歉,但它们都相当庞大,上面的存储过程查询了一个看起来更糟糕的视图(我甚至无法遵循)。

3 个答案:

答案 0 :(得分:3)

我多次遇到过类似的情况。和我一起....我通常是我自己的代码,我正在努力清理。

在做这样的事情时,请不要只考虑代码可读性。您还必须考虑对服务器的影响。通常,有多种方法可以编写产生相同结果的查询。在这些情况下,您应该选择执行速度最快的版本。如果这意味着您使用的是“丑陋”版本,那就这样吧。

显然,你看了原始代码并想了想,“嗯?”。这表明应该有代码注释。

我没有花太多时间查看代码,但似乎程序有各种可选参数(空字符串中的选项表示代码应该忽略该参数)。可以在不使用动态sql的情况下编写适应这种情况的代码,但该代码几乎总是执行得更慢。请阅读此处获取解释:Do you use Column=@Param OR @Param IS NULL in your WHERE clause? Don't, it doesn't perform

答案 1 :(得分:1)

我认为G Mastros为您提供了正确答案。我只想澄清我自己看到的。

首先,如果你的程序执行得更快,那么大数据就没有其他问题了。 其次,你的版本比第一个版本更难以理解。 通过为每个参数添加注释并设置它将更容易阅读。

所以我的答案是否定的,当然由于@filter的条件,它并没有归还所有供应商。只需在执行前打印它,以查看先前由给定参数产生的条件。

答案 2 :(得分:0)

尝试这样的事情:

ALTER PROCEDURE GetSignalMasterByFilter2(
  @planner varchar(50),
  @reorder int,
  @release int,
  @CMTTED varchar(50),
  @partid varchar(50),
  @global_short_dt int,
  @PRI_VENDOR_NAME varchar(50)
) as 

begin

  SELECT DISTINCT
    PRIMARY_VENDOR,
    case when PRI_VENDOR_NAME is null then PRIMARY_VENDOR else PRIMARY_VENDOR +' - '+ PRI_VENDOR_NAME end as PRI_VENDOR_NAME 
  FROM
    SignalReportView
  WHERE
       PRIMARY_VENDOR is not null
    and 
       (
           @Planner IS NULL 
        OR @planner = ''
        OR planner in (@planner))
    and 
    (  @reorder NOT IN (1,2,3) OR
        (@reorder=1 and REORDER_50='Y') or
        (@reorder=2 and REORDER_30='Y') or 
        (@reorder=3 and REORDER_POINT='Y') 
    )
    and 
    (
       @release NOT IN (1,2,3) OR
      (@release=1 and RELEASE_50='Y') or
      (@release=2 and RELEASE_30='Y') or
      (@release=3 and RELEASE_POINT='Y') 
    )
    and 
    (
      @CMTTED='View ALL' or
      0<CMTTED and ISNUMERIC(CMTTED)=1
    )
    and 
    ( 
       @global_short_dt NOT IN (1,2) OR
       (global_short_dt is not NULL AND @global_short_dt=1 AND CAST(ON_HAND as int) < CAST(CMTTED as int)) OR
       (global_short_dt is not NULL AND @global_short_dt=2 AND CAST(ON_HAND as int) < CAST(CMTTED as int) 
           and (CAST(QTY_IN_STATUS as float) + CAST(ON_ORDER as float) + CAST(ON_HAND as float)) < CAST(CMTTED as int))
    )
    and 
    (
      @partid IS NULL OR 
      @partid = '' OR
      PARTID like '%'+@partid+'%'
    )
    and 
    (
      @PRI_VENDOR_NAME IS NULL OR
      @PRI_VENDOR_NAME = '' OR
      PRI_VENDOR_NAME like '%'+@PRI_VENDOR_NAME+'%'
    )
  ORDER BY PRI_VENDOR_NAME

end

我认为我已经解决了所有的逻辑错误,但由于我没有任何表格,因此未经过测试。

至于性能,您必须检查两个版本并查看。无论如何都无法保证。