如何在SQL查询动态中使用公式?

时间:2016-06-14 03:52:15

标签: sql sql-server database

我需要你的帮助来动态创建查询, 如何从其他表中的公式填充数据值。

以下示例

Table A 
ID   Amount 
1    50 
2    40 
3    50 

Table B 
ID   FormulaID   VALUE 
X    1+2+3 
Y    1-2+3 

Result Expectation 
ID   FormulaID   VALUE 
X    1+2+3       140 
Y    1-2+3       60 

由于

2 个答案:

答案 0 :(得分:0)

我根据@Ashwin Nair的评论累了这个..这很好..

DECLARE @count INT = 1
DECLARE @Formula VARCHAR(200)
,@FormulaID VARCHAR(100)
,@IDstring VARCHAR(20)
,@operator VARCHAR(1)

CREATE TABLE #temp (RowID INT IDENTITY(1, 1),FormulaID VARCHAR(100),ID INT,SignOper VARCHAR(1),FORMULA VARCHAR(100),Flag INT,VALUE INT)
WHILE ( SELECT COUNT(*)FROM formulaT) >= @count
BEGIN
--Total formula records
SET @Formula = (SELECT formulaID FROM (SELECT ROW_NUMBER() OVER (ORDER BY ID) RowID,*FROM formulaT) a WHERE RowID = @count)
SET @FormulaID = (SELECT ID FROM (SELECT ROW_NUMBER() OVER (ORDER BY ID) RowID,* FROM formulaT) a WHERE RowID = @count)
DECLARE @ID INT
SET @ID = PATINDEX('%[^0-9]%', @Formula)
BEGIN
    WHILE @ID > 0
    BEGIN
        --spliting a formula --ID based on the charactors
        SET @IDstring = SUBSTRING(@Formula, 1, @ID - 1)
        SET @operator = SUBSTRING(@Formula, @ID, 1)
        SET @Formula = REPLACE(@Formula, @IDstring + @operator, '')
        INSERT INTO #temp
        SELECT @FormulaID,CAST(@IDstring AS INT),@operator,(SELECT CAST(Amount AS VARCHAR)  FROM amtt
                WHERE Id = CAST(@IDstring AS INT)),0,0
        SET @ID = PATINDEX('%[^0-9]%', @Formula)
        SET @IDstring = NULL
    END
    INSERT INTO #temp
    SELECT @FormulaID
        ,@Formula
        ,''
        ,(SELECT CAST(Amount AS VARCHAR)
            FROM amtt
            WHERE Id = CAST(@Formula AS INT))
        ,0,0
        --spliting a formula --ID based on the charactors
END
DECLARE @RowNo INT = (SELECT MIN(RowID) FROM #temp  WHERE FormulaID = @FormulaID)
SET @formula = ''
WHILE (SELECT MAX(RowID)FROM #temp WHERE FormulaID = @FormulaID) >= @RowNo
BEGIN
    SET @formula = @formula + (SELECT ISNULL(FORMULA + SignOper, SignOper)  FROM #temp  WHERE FormulaID = @FormulaID AND RowID = @RowNo)
    SET @RowNo = @RowNo + 1
END
INSERT INTO #temp
SELECT @FormulaID
    ,0,'',@formula,1,0
DECLARE @QUERY VARCHAR(250) = (SELECT 'sp_executesql N' + '''select ' + @formula + '''')
CREATE TABLE #CALC (VAL INT)
PRINT @QUERY
INSERT INTO #CALC
EXEC (@QUERY)
INSERT INTO #temp
SELECT @FormulaID
    ,0,'',@formula,2,(SELECT val FROM #CALC ) 
DROP TABLE #CALC
SET @Count = @count + 1
END

SELECT F.ID
,F.FORMULAID
,FORMULA
,VALUE
FROM formulaT F
INNER JOIN #temp T ON T.FORMULAID = F.ID
AND T.FLAG = 2

DROP TABLE #temp
--Output
--ID  FORMULAID  FORMULA      VALUE
--X   1+2+3      50+40+50     140
--Y   1-2+3      50-40+50     60
--Please let me know this answer is helpful or not..

由于 KARTHIK

答案 1 :(得分:0)

我认为,你的数据库设计是错误的。你真正的要求是什么,这会引导你考虑上面的数据库设计。你应该抛出更多的背景,你会在这个社区得到非常专业的解决方案。

我尝试了一些东西,只需测试其他样本数据并提供反馈。

DECLARE @TableB table(ID VARCHAR(50),FormulaID VARCHAR(50) ,VALUE INT)
INSERT INTO @TableB VALUES('X','1+2+3',NULL),('Y','1-2+3',NULL)

DECLARE @TableA table (ID INT,Amount INT)
INSERT INTO @TableA values(1,50 ),(2,40 ),(3,50 )

declare @input varchar(50)='a+b-c*d'
declare @delimiter varchar(50)='-+/*'


;WITH CTE
AS (
    SELECT id
        ,CASE 
            WHEN PATINDEX('%[' + @delimiter + ']%', FormulaID) > 0
                THEN substring(FormulaID, 0, PATINDEX('%[' + @delimiter + ']%', FormulaID))
            END numbers
        ,CASE 
            WHEN PATINDEX('%[' + @delimiter + ']%', FormulaID) > 0
                THEN substring(FormulaID, PATINDEX('%[' + @delimiter + ']%', FormulaID), 1)
            END delimeter
        ,CASE 
            WHEN PATINDEX('%[' + @delimiter + ']%', FormulaID) > 0
                THEN stuff(FormulaID, 1, PATINDEX('%[' + @delimiter + ']%', FormulaID), '')
            END input
        ,1 RN
        ,FormulaID
    FROM @TableB

    UNION ALL

    SELECT id
        ,CASE 
            WHEN PATINDEX('%[' + @delimiter + ']%', input) > 0
                THEN cast(substring(input, 0, PATINDEX('%[' + @delimiter + ']%', input)) AS VARCHAR(50))
            ELSE cast(input AS VARCHAR(50))
            END
        ,CASE 
            WHEN PATINDEX('%[' + @delimiter + ']%', input) > 0
                THEN substring(input, PATINDEX('%[' + @delimiter + ']%', input), 1)
            END
        ,CASE 
            WHEN PATINDEX('%[' + @delimiter + ']%', input) > 0
                THEN stuff(input, 1, PATINDEX('%[' + @delimiter + ']%', input), '')
            END
        ,RN + 1
        ,FormulaID
    FROM cte
    WHERE len(input) > 0
    )
    ,CTE1
AS (
    SELECT c.id
        ,c.numbers
        ,a.amount
        ,c.delimeter
        ,rn
        ,FormulaID
    FROM CTE C
    INNER JOIN @tableA a ON c.numbers = a.id
    )
    ,CTE4
AS (
    SELECT id
        ,formulaid
        ,amount
        ,delimeter
        ,numbers
        ,rn
    FROM cte1
    WHERE rn = 1

    UNION ALL

    SELECT a.id
        ,b.formulaid
        ,CASE 
            WHEN b.delimeter = '+'
                THEN b.amount + a.amount
            WHEN b.delimeter = '-'
                THEN b.amount - a.amount
            WHEN b.delimeter = '*'
                THEN b.amount * a.amount
            END
        ,a.delimeter
        ,a.numbers
        ,a.RN
    FROM cte1 a
    INNER JOIN cte4 b ON a.id = b.id
    WHERE a.rn = b.rn + 1
    )
SELECT id
    ,formulaid
    ,amount
FROM cte4
WHERE delimeter IS NULL