简体   繁体   English

SQL Python中的存储过程

[英]SQL Stored Procedure in Python

I am trying to write Python code to imitate the SQL stored produce dynamically.我正在尝试编写 Python 代码来模仿 SQL 动态存储的产品。 I am just in the begging.我只是在乞讨。 I have successfully connected to SQL DB using pyodbc and get data into Pandas df.我已使用 pyodbc 成功连接到 SQL DB 并将数据输入 Pandas df。 1. I have successfully getting all SurveyId from Survey table to construct all SurveyId list in Python. 1.我已经成功地从Survey表中获取所有SurveyId来构建Python中的所有SurveyId列表。 This is the Survey Table:这是调查表: 调查表 With this code, I get all SurveyId list from Survey table:使用此代码,我从调查表中获取所有 SurveyId 列表:

def surveyCursor():
    query_GetSurveyId = 'SELECT SurveyId FROM Survey'
    cursor = sql_conn.cursor()
    cursor.execute(query_GetSurveyId)
    row = [item[0] for item in cursor.fetchall()]
    return row

surveyId_list = surveyCursor()
#print(surveyId_list)
  1. Now with the next function, I already have this SQL stored procedure as followed:现在有了下一个 function,我已经有了这个 SQL 存储过程,如下所示:
DECLARE currentQuestionCursor CURSOR FOR
            SELECT *
                    FROM
                    (
                        SELECT
                            SurveyId,
                            QuestionId,
                            1 as InSurvey
                        FROM
                            SurveyStructure
                        WHERE
                            SurveyId = @currentSurveyId
                        UNION
                        SELECT 
                            @currentSurveyId as SurveyId,
                            Q.QuestionId,
                            0 as InSurvey
                        FROM
                            Question as Q
                        WHERE NOT EXISTS
                        (
                            SELECT *
                            FROM SurveyStructure as S
                            WHERE S.SurveyId = @currentSurveyId AND S.QuestionId = Q.QuestionId
                        )
                    ) as t
                    ORDER BY QuestionId;

I need to use this in Python, therefore I created this function:我需要在 Python 中使用它,因此我创建了这个 function:

def CheckInSurvey(SurveyList):
    for surveyId in SurveyList:
        if surveyId < len(SurveyList):
            query_QuestionInSurvey = 'SELECT * FROM (SELECT SurveyId, QuestionId, 1 as InSurvey \
                        FROM SurveyStructure WHERE SurveyId = ' + str(surveyId) + \
                        ' UNION SELECT ' + str(surveyId) + ' as SurveyId, Q.QuestionId, 0 as InSurvey \
                        FROM Question as Q WHERE NOT EXISTS ( SELECT * FROM SurveyStructure as S \
                        WHERE S.SurveyId = ' + str(surveyId) + ' AND S.QuestionId = Q.QuestionId)) as t UNION '
            cursor = sql_conn.cursor()
            cursor.execute(query_QuestionInSurvey)
            row = [item[0] for item in cursor.fetchall()]
            #checkQuestionInSurvey = query_QuestionInSurvey
        else:
            query_QuestionInSurvey_LastRow = 'SELECT * FROM (SELECT SurveyId, QuestionId, 1 as InSurvey \
                        FROM SurveyStructure WHERE SurveyId = ' + str(surveyId) + \
                        ' UNION SELECT ' + str(surveyId) + ' as SurveyId, Q.QuestionId, 0 as InSurvey \
                        FROM Question as Q WHERE NOT EXISTS ( SELECT * FROM SurveyStructure as S \
                        WHERE S.SurveyId = ' + str(surveyId) + ' AND S.QuestionId = Q.QuestionId)) as t ORDER BY QuestionId '
            cursor = sql_conn.cursor()
            cursor.execute(query_QuestionInSurvey_LastRow)
            row = [item[0] for item in cursor.fetchall()]
            #checkQuestionInSurvey = query_QuestionInSurvey_LastRow
        surveyId += 1
    return row

InSurveyList = CheckInSurvey(surveyId_list)

For this function, I want to check the current QuestionId to compared to the Survey Structure table:对于这个 function,我想检查当前 QuestionId 以与调查结构表进行比较: 调查结构表 If current QuestionId is in the Survey Structure table, then set InSurvey to 1 or else set InSurvey to 0.如果当前 QuestionId 在调查结构表中,则将 InSurvey 设置为 1,否则将 InSurvey 设置为 0。

I want to get a table like this to put in dataframe:我想得到一张这样的表来放入 dataframe: 调查

However, when I compile my code, I get this error:但是,当我编译我的代码时,我得到了这个错误:

错误Python

Could you please let me know what can be the issue here?你能告诉我这里可能是什么问题吗? Thank you very much in advance.非常感谢您提前。

Here is the whole Python code:这是整个 Python 代码:

import pandas as pd
import pyodbc as odbc

# Make a connection to database using pyodbc, then store the query in a variable then put it in pandas dataframe
# To check which ODBC Driver to use:
# 1. From Start menu, go to ODBC Data Sources
# 2. Clieck the Drivers tab, then find SQL Server ODBC Driver in the list of ODBC drivers that installed on the system
sql_conn = odbc.connect('DRIVER={ODBC Driver 17 for SQL Server};SERVER=LAPTOP-NAD8U5G4;DATABASE=Survey_Sample_A19;Trusted_Connection=yes')
query = "SELECT * FROM [Answer]"
sql_to_df = pd.read_sql(query, sql_conn)

#print(sql_to_df.head(3))

# Section 1: Stored Procedue
# Inner loop:
# 1. Iterate in the Survey table to get all SurveyId
# 2. After we get all SurveyId, this will become @currentSurveyId to be used in the loop below in step 3
# Iterate through each Question using "currentQuestionCursor"
# 3. Check if current QuestionId is in SurveyStructure table:
# Yes -> set InSurvey = 1
#     Then, get current QuestionId from Answer table (also SurveyId from Answer table?????), then update Answer string "strQueryTemplateForAnswerColumn", replace with 
#     strColumnsQueryPart AS ANS_Q(currentQuestionID)
# No -> set InSurvey = 0
#     Then, update Answer string "strQueryTemplateForNullColumnn" to
#     NULL AS ANS_Q(currentQuestionID)
# 4. Select SurveyId, AnswerId, then update Answer string
# 5. After the last row, concatenate Answer string "strQueryTemplateForAnswerColumn" with ","
# 6. Remove the cursor "currentQuestionCursor" when finish the Inner

def surveyCursor():
    query_GetSurveyId = 'SELECT SurveyId FROM Survey'
    cursor = sql_conn.cursor()
    cursor.execute(query_GetSurveyId)
    row = [item[0] for item in cursor.fetchall()]
    return row

surveyId_list = surveyCursor()
#print(surveyId_list)

def CheckInSurvey(SurveyList):
    for surveyId in SurveyList:
        if surveyId < len(SurveyList):
            query_QuestionInSurvey = 'SELECT * FROM (SELECT SurveyId, QuestionId, 1 as InSurvey \
                        FROM SurveyStructure WHERE SurveyId = ' + str(surveyId) + \
                        ' UNION SELECT ' + str(surveyId) + ' as SurveyId, Q.QuestionId, 0 as InSurvey \
                        FROM Question as Q WHERE NOT EXISTS ( SELECT * FROM SurveyStructure as S \
                        WHERE S.SurveyId = ' + str(surveyId) + ' AND S.QuestionId = Q.QuestionId)) as t UNION '
            cursor = sql_conn.cursor()
            cursor.execute(query_QuestionInSurvey)
            row = [item[0] for item in cursor.fetchall()]
            #checkQuestionInSurvey = query_QuestionInSurvey
        else:
            query_QuestionInSurvey_LastRow = 'SELECT * FROM (SELECT SurveyId, QuestionId, 1 as InSurvey \
                        FROM SurveyStructure WHERE SurveyId = ' + str(surveyId) + \
                        ' UNION SELECT ' + str(surveyId) + ' as SurveyId, Q.QuestionId, 0 as InSurvey \
                        FROM Question as Q WHERE NOT EXISTS ( SELECT * FROM SurveyStructure as S \
                        WHERE S.SurveyId = ' + str(surveyId) + ' AND S.QuestionId = Q.QuestionId)) as t ORDER BY QuestionId '
            cursor = sql_conn.cursor()
            cursor.execute(query_QuestionInSurvey_LastRow)
            row = [item[0] for item in cursor.fetchall()]
            #checkQuestionInSurvey = query_QuestionInSurvey_LastRow
        surveyId += 1
    return row

InSurveyList = CheckInSurvey(surveyId_list)

And, the SQL stored procedure:并且,SQL 存储过程:

USE [Survey_Sample_A18]
GO
/****** Object:  UserDefinedFunction [dbo].[fn_GetAllSurveyDataSQL]    Script Date: 2/8/2020 4:31:13 PM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:      <Author,,Name>
-- Create date: <Create Date, ,>
-- Description: <Description, ,>
-- =============================================
CREATE OR ALTER FUNCTION [dbo].[fn_GetAllSurveyDataSQL]() 
RETURNS nvarchar(max)
AS
BEGIN

    DECLARE @strQueryTemplateForAnswerColumn nvarchar(max);
    DECLARE @strQueryTemplateForNullColumnn nvarchar(max);
    DECLARE @strQueryTemplateOuterUnionQuery nvarchar(max);
    DECLARE @currentSurveyId int;

    -- WHEN YOU WRITE DYNAMIC SQL IN STRING VARIABLES
    -- IT'S LIKELY THAT YOU WILL CONCATENATE STRINGS LATER ON IN THE PROCESS
    -- SO, BE CAREFULL OF LEAVING SPACES BEFORE AND AFTER THE QUERY TEXT
    SET @strQueryTemplateForAnswerColumn = '
            COALESCE(
                (
                    SELECT a.Answer_Value
                    FROM Answer as a
                    WHERE
                        a.UserId = u.UserId
                        AND a.SurveyId = <SURVEY_ID>
                        AND a.QuestionId = <QUESTION_ID>
                ), -1) AS ANS_Q<QUESTION_ID> ';

    SET @strQueryTemplateForNullColumnn = ' NULL AS ANS_Q<QUESTION_ID> '

    SET @strQueryTemplateOuterUnionQuery = '
            SELECT
                    UserId
                    , <SURVEY_ID> as SurveyId
                    , <DYNAMIC_QUESTION_ANSWERS>
            FROM
                [User] as u
            WHERE EXISTS
            (
                    SELECT *
                    FROM Answer as a
                    WHERE u.UserId = a.UserId
                    AND a.SurveyId = <SURVEY_ID>
            )
    ';

    DECLARE @strCurrentUnionQueryBlock nvarchar(max);
    SET @strCurrentUnionQueryBlock = ''


    DECLARE @strFinalQuery nvarchar(max);
    SET @strFinalQuery = ''

    --Cursor variables are the only ones to not have an @ in front of their names
    DECLARE surveyCursor CURSOR FOR
                            SELECT SurveyId
                            FROM Survey
                            ORDER BY SurveyId;

    OPEN surveyCursor; -- when opened, the cursor is before the first row
    FETCH NEXT FROM surveyCursor INTO @currentSurveyId; --first row read

    WHILE @@FETCH_STATUS = 0 --AS LONG AS FETCH_STATUS IS EQUAL TO 0 --> there's a row to read
    BEGIN

        -- MAIN LOOP, OVER ALL THE SURVEYS

        -- FOR EACH SURVEY, IN @currentSurveyId, WE NEED TO CONSTRUCT THE ANSWER COLUMN QUERIES

        -- Another iteration, over the questions of the survey
        -- inner loop, another cursor

        --I want a resultset of SurveyId, QuestionId, flag InSurvey indicating whether
        -- the question is in the survey structure
        DECLARE currentQuestionCursor CURSOR FOR
            SELECT *
                    FROM
                    (
                        SELECT
                            SurveyId,
                            QuestionId,
                            1 as InSurvey
                        FROM
                            SurveyStructure
                        WHERE
                            SurveyId = @currentSurveyId
                        UNION
                        SELECT 
                            @currentSurveyId as SurveyId,
                            Q.QuestionId,
                            0 as InSurvey
                        FROM
                            Question as Q
                        WHERE NOT EXISTS
                        (
                            SELECT *
                            FROM SurveyStructure as S
                            WHERE S.SurveyId = @currentSurveyId AND S.QuestionId = Q.QuestionId
                        )
                    ) as t
                    ORDER BY QuestionId;

        DECLARE @currentSurveyIdInQuestion int;
        DECLARE @currentQuestionID int;
        DECLARE @currentInSurvey int;

        OPEN currentQuestionCursor;
        --When fetching into local variable, the column order of the select clause must be followed
        FETCH NEXT FROM currentQuestionCursor INTO @currentSurveyIdInQuestion,
                                                    @currentQuestionID,
                                                    @currentInSurvey;

        DECLARE @strColumnsQueryPart nvarchar(max);

        SET @strColumnsQueryPart = '';
        WHILE @@FETCH_STATUS = 0
        BEGIN
            --the "global" variable @@FETCH_STATUS GETS LOCALISED BETWEEN BEGIN --- END BLOCK

            --INNER LOOP IETERATES OVER THE QUESTION

            --IS THE CURRENT QUESTION (inner loop) IN THE CURRENT SURVEY (outer loop)

            IF @currentInSurvey = 0 -- CURRENT QUESTION IS NOT IN THE CURRENT SURVEY
            BEGIN
                --THEN BLOCK
                -- SPECIFICATION: THE VALUES IN THIS COLUMN WILL BE NULL
                SET @strColumnsQueryPart = @strColumnsQueryPart +
                        REPLACE(@strQueryTemplateForNullColumnn, '<QUESTION_ID>',
                                @currentQuestionID);
            END
            ELSE
            BEGIN
                SET @strColumnsQueryPart = @strColumnsQueryPart +
                    REPLACE(@strQueryTemplateForAnswerColumn, '<QUESTION_ID>',
                            @currentQuestionID);
            END;

            FETCH NEXT FROM currentQuestionCursor INTO @currentSurveyIdInQuestion,
                                                    @currentQuestionID,
                                                    @currentInSurvey;

        IF @@FETCH_STATUS = 0
        BEGIN
            -- Place a comma between column statements, except for the last one
            SET @strColumnsQueryPart = @strColumnsQueryPart + ' , ';
        END;

        END; -- END OF CLOSE INNER LOOP WHILE
        CLOSE currentQuestionCursor;
        DEALLOCATE currentQuestionCursor;


        --BACK IN THE OUTER LOOP OVER SURVEYS
        SET @strCurrentUnionQueryBlock = 
            REPLACE(@strQueryTemplateOuterUnionQuery,
                    '<DYNAMIC_QUESTION_ANSWERS>',
                    @strColumnsQueryPart);

        SET @strCurrentUnionQueryBlock = 
            REPLACE(@strCurrentUnionQueryBlock, 
                        '<SURVEY_ID>', @currentSurveyId);

        SET @strFinalQuery = @strFinalQuery + @strCurrentUnionQueryBlock

        FETCH NEXT FROM surveyCursor INTO @currentSurveyId;

        IF @@FETCH_STATUS = 0 
        BEGIN
            SET @strFinalQuery = @strFinalQuery + ' UNION ' ;
        END;

    END;

    CLOSE surveyCursor;
    DEALLOCATE surveyCursor;

    --calling the system store procedure sp_executesql
    --recommended by Microsoft should the text of your dunamic query > 4000 chars
     RETURN @strFinalQuery;


END

In CheckInSurvey the last UNION in query_QuestionInSurvey might cause a problem.CheckInSurvey中, query_QuestionInSurvey中的最后一个UNION可能会导致问题。

def CheckInSurvey(SurveyList):
for surveyId in SurveyList:
    if surveyId < len(SurveyList):
        query_QuestionInSurvey = 'SELECT * FROM (SELECT SurveyId, QuestionId, 1 as InSurvey \
                    FROM SurveyStructure WHERE SurveyId = ' + str(surveyId) + \
                    ' UNION SELECT ' + str(surveyId) + ' as SurveyId, Q.QuestionId, 0 as InSurvey \
                    FROM Question as Q WHERE NOT EXISTS ( SELECT * FROM SurveyStructure as S \
                    WHERE S.SurveyId = ' + str(surveyId) + ' AND S.QuestionId = Q.QuestionId)) as t UNION '
#       See HERE! ----------------------------------------------------------------------------------^
        cursor = sql_conn.cursor()
        cursor.execute(query_QuestionInSurvey)
...

I used another way to execute my SQL query: query_questionInSurvey = pd.read_sql_query(InSurveyList, sql_conn) Instead of cursor.execute()我用另一种方式来执行我的 SQL 查询: query_questionInSurvey = pd.read_sql_query(InSurveyList, sql_conn) 而不是 cursor.execute()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM