【问题标题】:Change string to min/max values to generate middle value as a result将字符串更改为最小/最大值以生成中间值作为结果
【发布时间】:2017-02-22 04:59:00
【问题描述】:

编辑:我意识到我试图编辑的字段是 nvarchar(max),并且包含大量垃圾数据,而我试图开始工作的扩展作为逗号分隔值的单一角色。 ..按照我的要求去做是不太可能的。非常感谢所有提供帮助的人!

在我问之前,请知道我对数据库具有只读访问权限并且无法写入临时表...因为这是我想到的一种解决方案...

我目前正在使用以下查询:

USE Database
GO
DECLARE @dn VARCHAR(13)
SET @dn = '%15555555555%'
SELECT switch_extension, line_number, system_configurations.name, system_configurations.description, lines.system_id
FROM lines, system_configurations
WHERE lines.system_id=system_configurations.system_id
AND switch_extension like @dn
AND lines.status = 1

SELECT distinct CS.name, CTD.cti_data_value
FROM cti_source AS CS with (nolock)
  JOIN cti_source_data AS CTD with (nolock)ON CS.cti_source_id = CTD.cti_source_id
--WHERE CS.status = 1 
    AND (CTD.cti_data_name = 'Agent_Ext'or CTD.cti_data_name = 'IVR_Ext') 
    AND CTD.cti_data_value LIKE @dn

问题是,CTD.cti_data_value 可以包含存储为字符串的范围,而不是具有单个值的新行,例如 '15551112222-15551113333' 而不是 15551112222、15551112223、...

如果我在此范围内查询@dn(除非范围的开头或结尾),我没有得到任何结果(在上面的示例中,如果我搜索 15551112345,即使它存在,也不会有结果在“范围”内)。

有没有办法改变这个来获得这个单个字符串中的最小值/最大值,然后在最小值/最大值之间找到一个结果,或者我是在一条小溪上吗?

【问题讨论】:

    标签: sql sql-server-2008


    【解决方案1】:

    您需要将 CTD.cti_data_value 中的值拆分为一个范围的下限值和上限值。在 where 子句中更改这一行...

    AND CTD.cti_data_value LIKE @dn
    

    到这个...

    AND @dn BETWEEN
        CASE WHEN  CHARINDEX('-', CTD.cti_data_value) > 0 THEN
            SUBSTRING(CTD.cti_data_value, 1,  CHARINDEX('-', CTD.cti_data_value) - 1)
        ELSE
            CTD.cti_data_value
        END 
      AND 
        CASE WHEN  CHARINDEX('-', CTD.cti_data_value) > 0 THEN
            SUBSTRING(CTD.cti_data_value, CHARINDEX('-', CTD.cti_data_value) + 1, LEN(CTD.cti_data_value) - CHARINDEX('-', CTD.cti_data_value))
        ELSE
            CTD.cti_data_value
        END
    

    这将适应具有一系列值(例如 X-Y)或只是一个奇异值。我必须警告您,比较字符串变量(例如 CHAR、VARCHAR 等)中的数值可能会导致不可预测的结果。例如,比较字符串变量中的数值时,“2”大于“10”。

    如果您绝对确定 CTD.cti_data_value 中的数字将始终是相同的位数,您就可以了。如果不是这种情况,您应该像这样将您的数值转换为 INT(或任何适合您的情况)...

    AND CONVERT(INT, @dn) BETWEEN
        CONVERT(INT, CASE WHEN  CHARINDEX('-', CTD.cti_data_value) > 0 THEN
            SUBSTRING(CTD.cti_data_value, 1,  CHARINDEX('-', CTD.cti_data_value) - 1)
        ELSE
            CTD.cti_data_value
        END)
      AND 
        CONVERT(INT, CASE WHEN  CHARINDEX('-', CTD.cti_data_value) > 0 THEN
            SUBSTRING(CTD.cti_data_value, CHARINDEX('-', CTD.cti_data_value) + 1, LEN(CTD.cti_data_value) - CHARINDEX('-', CTD.cti_data_value))
        ELSE
            CTD.cti_data_value
        END)
    

    【讨论】:

    • 位数会有所不同。它们可以是长度为 8 或 11 个字符的单个条目,或者如果它是一个范围,它将是 17 或 23(包括“-”)。我尝试使用给定的第二种解决方案,但它给出了一个错误“将数据类型 varchar 转换为 int 时出错”(然后我尝试了 bigint,但这也不起作用)
    • 您要转换的值中是否有非数字字符?
    • 它有一个 % 作为通配符,但我什至删除了它来尝试。 pastebin.com/15d5q9DJ 是示例,返回“Msg 8114, Level 16, State 5, Line 10 Error convert data type nvarchar to bigint.”
    • CTD.cti_data_value的数据类型和大小是多少?
    • 这是一个分机号/电话号码或它们的范围。数据类型是 nvarchar(60)。例如,这可以是“11081111”、“15551112222”、“11081111-11081114”、“15551112222-15551112223”。这是有道理的,为什么转换不起作用,因为“-”可以包含在字符串中并且是非数字的。该软件的先前版本每行有一个分机/电话号码,如果存在,结果会出现。这个将它存储为一个字符串,除非你有幸在一个范围内有第一个/最后一个数字,否则它会在不使用前端手动查找的情况下显示为不存在。
    【解决方案2】:

    我认为您需要将字符串拆分为不同的组件。见Splitting the string in sql server

    现在这是一个读/写版本,但您正在做类似的事情。然后您的最小值将始终是分隔符之前的位,而您的最大值将始终是之后的位。

    第二种选择是在 .net 中编写一个用户定义的函数,该函数将接收字符串,进行完整性检查,并返回您正在寻找的适当值。如果性能至关重要,则使用 CLR 实际上可能是一件好事。

    但如果没有 CLR,您需要:

    1. 找到分隔符的位置(- 在这种情况下)
    2. 将字符串取到,但不包括分隔符作为最小值,并且
    3. 以分隔符后面的字符串为最大值。

    【讨论】:

      【解决方案3】:

      您可以使用 CTE 而不是表格,并通过拆分它们来计算范围内的最小值和最大值,并检查您的 @dn 是否在这些值之间。确保 @dn 被声明为 BIGINT。 我假设您的值始终为 BIGINT(您的值超过 INT)。

      这是一个证明这一点的工作示例:

          DECLARE @dn BIGINT  = 15555555555;
          --SET       @dn         = 15551112223;
      
          WITH cti_source_data AS (
              SELECT  *
              FROM    (VALUES ('15555555555'), ('15551112222-15551113333')) AS V (cti_data_value)
      
          ), CTD AS (
              SELECT  *, 
                      CASE CHARINDEX('-', cti_data_value) WHEN 0 THEN cti_data_value ELSE SUBSTRING(cti_data_value, 0, CHARINDEX('-', cti_data_value)) END MinValue,
                      SUBSTRING(cti_data_value, CHARINDEX('-', cti_data_value)+1, LEN(cti_data_value)) MaxValue
              FROM    cti_source_data
          )
          SELECT * 
          FROM    CTD
          WHERE   @dn BETWEEN MinValue AND MaxValue
      

      至于您的查询,您需要用计算这些最小值和最大值的 CTE 替换表:

          DECLARE @dn BIGINT  = 15555555555;
      
          SELECT switch_extension, line_number, system_configurations.name, system_configurations.description, lines.system_id
          FROM lines, system_configurations
          WHERE lines.system_id=system_configurations.system_id
          AND switch_extension like '%' + @dn '%'
          AND lines.status = 1
      
          ;WITH CTD AS (
              SELECT  *, 
                      CASE CHARINDEX('-', cti_data_value) WHEN 0 THEN cti_data_value ELSE SUBSTRING(cti_data_value, 0, CHARINDEX('-', cti_data_value)) END MinValue,
                      SUBSTRING(cti_data_value, CHARINDEX('-', cti_data_value)+1, LEN(cti_data_value)) MaxValue
              FROM    cti_source_data (NOLOCK)
          )
          SELECT distinct CS.name, CTD.cti_data_value
          FROM cti_source AS CS with (nolock)
            JOIN ON CS.cti_source_id = CTD.cti_source_id
          --WHERE CS.status = 1 
              AND (CTD.cti_data_name = 'Agent_Ext'or CTD.cti_data_name = 'IVR_Ext') 
              --AND CTD.cti_data_value LIKE @dn
              AND @dn BETWEEN CTD.MinValue AND CTD.MaxValue
      

      【讨论】:

        猜你喜欢
        • 2022-09-27
        • 2011-12-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-28
        • 1970-01-01
        • 2013-04-10
        相关资源
        最近更新 更多