SQL-仅选择包含字符串的列中的数字

时间:2015-03-12 06:51:31

标签: sql sql-server sql-server-2008 patindex charindex

我有一个这样的表(第一列包含id adn第二列是varchar,其中包含字符串(一些公式)) -

column_id       column_formula
4686         4686 = 4684 - 4685 
4687         4687 = ( 4681 / 1.205 / 4684 * 1000 ) 
4717         4717 = ( 4711 + 4712 + 4713 + 4714 + 4715 + 4716 )/6 
4719         4719 = abs( 4716 - 4715 ) 
4787         4787 = max(max(max(max(max( 4780 , 4781 ), 4782 ), 4783 ), 4784 ), 4785 ) - min(min(min(min(min( 4780 , 4781 ), 4782 ), 4783 ), 4784 ), 4785 ) 

现在需要一些查询,它将提供如下输出 -

col1    col2
4686    4684      
4686    4685      
4687    4681 
4687    4684 
4717    4711
4717    4712

依此类推..(只有小于4位数的数字,如1.205左右......和1000)

很难在这个上使用patindex

any1可以给我一个解决方案吗

2 个答案:

答案 0 :(得分:1)

您需要具有将逗号分隔的字符串拆分为单独行的函数。以下是Jeff Moden的DelimitedSplit8K函数:

CREATE FUNCTION [dbo].[DelimitedSplit8K](
    @pString NVARCHAR(4000), @pDelimiter NCHAR(1)
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS (
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL 
    SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,E2(N) AS (SELECT 1 FROM E1 a, E1 b)
,E4(N) AS (SELECT 1 FROM E2 a, E2 b)
,cteTally(N) AS(
    SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
,cteStart(N1) AS(
    SELECT 1 UNION ALL 
    SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(
SELECT 
    s.N1,
    ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
SELECT 
    ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
    Item       = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l

我的想法是REPLACE首先是所有数学字符:+-*/(,{{1带有逗号)的{​​}},=。这是必需的,因此我们可以稍后拆分操作数。接下来调用拆分器函数以逗号,作为分隔符拆分行。完成拆分后,使用,过滤所有数值。您希望将NOT LIKE '[^0-9]%' AND Item <> ''的结果设为INSERT。然后,来自Temp Table的{​​{1}} SELECTTemp Table子句:

WHERE

<强> RESULT

;WITH cteSanitized AS(
    SELECT
        t.Column_Id,
        Item = 
            REPLACE(
                REPLACE(
                    REPLACE(
                        REPLACE(
                            REPLACE(
                                REPLACE(
                                    REPLACE(
                                        SUBSTRING(t.Column_Formula, CHARINDEX('=', t.Column_Formula), LEN(t.Column_Formula) - CHARINDEX('=', t.Column_Formula) + 1)
                                        ,'+',','
                                    ), '-',','
                                ), '/',','
                            ), '*',','
                        ), ')', ',)'
                    ), '(',','
                ), '=',','
            )
    FROM Test t
)
,CteSplitted AS(
    SELECT
        s.Column_Id,
        ItemNumber,
        Item = LTRIM(RTRIM(x.Item))
    FROM cteSanitized s
    CROSS APPLY dbo.DelimitedSplit8K(s.Item, ',') x
)
SELECT 
    Column_Id,
    ItemNumber,
    Item = CONVERT(NUMERIC, ITEM)
INTO #TempTable
FROM CteSplitted
WHERE 
    Item  NOT LIKE '[^0-9]%'
    AND Item <> ''

SELECT DISTINCT
    Col1 = Column_Id,
    Col2 = Item
FROM #TempTable
WHERE Item > 1000

DROP TABLE #TempTable

答案 1 :(得分:1)

这是没有用户定义功能的解决方案。它应该快得多:

DECLARE @t TABLE ( ID INT, F NVARCHAR(MAX) )

INSERT  INTO @t
VALUES  ( 4686, '4686 = 4684 - 4685' ),
        ( 4687, '4687 = ( 4681 / 1.205 / 4684 * 1000 )' ),
        ( 4717, '4717 = ( 4711 + 4712 + 4713 + 4714 + 4715 + 4716 )/6' ),
        ( 4719, '4719 = abs( 4716 - 4715 ) ' ),
        ( 4787,
          '4787 = max(max(max(max(max( 4780 , 4781 ), 4782 ), 4783 ), 4784 ), 4785 ) - min(min(min(min(min( 4780 , 4781 ), 4782 ), 4783 ), 4784 ), 4785 )' )

DECLARE @chars TABLE ( ID INT, c NVARCHAR(MAX) )
INSERT  INTO @chars
VALUES  ( 1, ' ' ),
        ( 2, '(' ),
        ( 3, ')' ),
        ( 4, '/' ),
        ( 5, '*' ),
        ( 6, '+' ),
        ( 7, '-' ),
        ( 8, 'max' ),
        ( 9, 'min' ),
        ( 10, 'abs' ),
        ( 11, '=' )

DECLARE @count INT
SELECT  @count = COUNT(*) FROM    @chars;

WITH    recursion
          AS ( SELECT   t.ID ,
                        REPLACE(F, ' ', ',') + ',' AS F ,
                        1 AS CharID
               FROM     @t t
               UNION ALL
               SELECT   t.ID ,
                        REPLACE(t.F, c.c, ',') AS F ,
                        t.CharID + 1 AS CharID
               FROM     recursion t
                        JOIN @chars c ON c.ID = t.CharID + 1
               WHERE    t.CharID < @count
             ),
        commastrings
          AS ( SELECT   ID ,
                        STUFF(F, LEN(F), 1, '') AS F
               FROM     ( SELECT    ID ,
                                    REPLACE(REPLACE(REPLACE(F, ',', '{}'), '}{', ''), '{}', ',') AS F
                          FROM      recursion
                          WHERE     CharID = @count
                        ) a
             ),
        final ( ID, Number, rght, idx )
          AS ( SELECT   t.ID ,
                        LEFT(t.F, CHARINDEX(',', t.F) - 1) ,
                        SUBSTRING(t.F, CHARINDEX(',', t.F) + 1, LEN(t.F)) ,
                        0
               FROM     commastrings t
               UNION ALL
               SELECT   c.id ,
                        CASE WHEN CHARINDEX(',', c.rght) = 0 THEN c.rght
                             ELSE LEFT(c.rght, CHARINDEX(',', c.rght) - 1)
                        END ,
                        CASE WHEN CHARINDEX(',', c.rght) > 0
                             THEN SUBSTRING(c.rght, CHARINDEX(',', c.rght) + 1, DATALENGTH(c.rght))
                             ELSE ''
                        END ,
                        idx + 1
               FROM     final c
               WHERE   LENGTH(c.rght) > 0
             )
    SELECT DISTINCT
            ID ,
            Number
    FROM    final
    WHERE   idx <> 0
            AND CAST(Number AS DECIMAL(20, 10)) > 1000