Mysql 如何在pd.read\u SQL\u查询中一次执行多个SQL命令?

Mysql 如何在pd.read\u SQL\u查询中一次执行多个SQL命令?,mysql,python-3.x,sqlalchemy,pymysql,Mysql,Python 3.x,Sqlalchemy,Pymysql,让我创建一个要讨论的用例 CREATE DATABASE sample; USE sample; CREATE TABLE quote ( `id` int(2) unsigned NOT NULL AUTO_INCREMENT, `code` text , `date` date DEFAULT NULL, `close` double DEFAULT NULL, PRIMARY KEY (`id`) ) ; INSERT INTO quote (`code`, `da

让我创建一个要讨论的用例

CREATE  DATABASE sample;
USE sample;
CREATE TABLE quote (
  `id` int(2) unsigned NOT NULL AUTO_INCREMENT,
  `code` text ,
  `date` date DEFAULT NULL,
  `close` double DEFAULT NULL,
  PRIMARY KEY (`id`)
) ;

INSERT INTO quote (`code`, `date`, `close`)
VALUES ('epm', '20200824', 2.64); 
INSERT INTO quote (`code`, `date`, `close`)
VALUES ('dss', '20200824', 6.4); 
使用sqlalchemy只执行一个sql命令很简单

import pandas as pd 
from sqlalchemy import create_engine
user = 'root'
mysql_pass = 'your mysql passwd'
mysql_ip = '127.0.0.1'
engine = create_engine("mysql+pymysql://{}:{}@{}:3306".format(user,mysql_pass,mysql_ip))
cmd_one_line_sql = 'select * from sample.quote;'
df = pd.read_sql_query(cmd_one_line_sql,con = engine)
df 
   id code        date  close
0   1  epm  2020-08-24   2.64
1   2  dss  2020-08-24   6.40
我得到了期望的结果,现在cmd包含多个sql命令,为了简单起见,它只包含两行

cmd_multi_lines_sql = 'use sample;select * from quote;'
cmd\u multi\u line\u sql
只是将
cmd\u one\u line\u sql
拆分为两个。
我根据手册重写了代码片段:

获取以下错误信息:

Traceback (most recent call last):
  File "<stdin>", line 3, in <module>
  File "/usr/local/lib/python3.5/dist-packages/pymysql/cursors.py", line 170, in execute
    result = self._query(query)
  File "/usr/local/lib/python3.5/dist-packages/pymysql/cursors.py", line 328, in _query
    conn.query(q)
  File "/usr/local/lib/python3.5/dist-packages/pymysql/connections.py", line 517, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
  File "/usr/local/lib/python3.5/dist-packages/pymysql/connections.py", line 732, in _read_query_result
    result.read()
  File "/usr/local/lib/python3.5/dist-packages/pymysql/connections.py", line 1075, in read
    first_packet = self.connection._read_packet()
  File "/usr/local/lib/python3.5/dist-packages/pymysql/connections.py", line 684, in _read_packet
    packet.check_error()
  File "/usr/local/lib/python3.5/dist-packages/pymysql/protocol.py", line 220, in check_error
    err.raise_mysql_exception(self._data)
  File "/usr/local/lib/python3.5/dist-packages/pymysql/err.py", line 109, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.ProgrammingError: (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'select * from quote' at line 1")

它输出相同的错误信息。如何修复它?

您面临的问题是:

  • 您需要将
    MULTI_语句
    标志传递给PyMySQL,然后
  • read\u sql\u query
    假定第一个结果集包含数据帧的数据,而对于匿名代码块,这可能不是真的
  • 您可以创建自己的PyMySQL连接并检索以下数据:

    将熊猫作为pd导入
    导入pymysql
    从pymysql.constants导入客户端
    连接信息={
    “主机”:“本地主机”,
    “港口”:3307,
    “用户”:“根用户”,
    “密码”:“嘟嘟”,
    “数据库”:“mydb”,
    “client_标志”:client.MULTI_语句,
    }
    cnxn=pymysql.connect(**连接信息)
    crsr=cnxn.cursor()
    sql=”“”\
    创建临时表tmp(id int主键,txt varchar(20))
    ENGINE=InnoDB默认字符集=utf8mb4 COLLATE=utf8mb4\u unicode\u ci;
    在tmp(id,txt)值中插入(1,'foo'),(2,'crmk∏Α!');
    从tmp中选择id、txt;
    """
    crsr.execute(sql)
    尝试次数=5
    结果=无
    对于范围内的i(尝试次数):
    结果=crsr.fetchall()
    如果结果为:
    打破
    crsr.nextset()
    如果没有结果:
    打印(f)(在{num_尝试}次尝试后未找到结果)
    其他:
    df=pd.DataFrame(结果,列=[x[0]表示crsr.description中的x])
    打印(df)
    “”“控制台输出:
    id文本
    0 1 foo
    1   2  ΟΠΑ!
    """
    
    (编辑)其他注释:

    注1:如中所述,您可以使用SQLAlchemy的
    create\u engine
    方法的
    connect\u args
    参数传递
    MULTI\u语句
    标志。如果您需要一个SQLAlchemy
    引擎
    对象来处理其他事情(例如,
    to_sql
    ),那么这可能比直接创建自己的PyMySQL连接更好


    注2:
    num\u tries
    可以任意大;这只是一种避免无止境循环的方法。如果我们需要跳过前n个空结果集,那么我们需要多次调用
    nextset
    ,一旦我们找到了非空结果集,我们就
    打破循环。

    经过soem研究并在github询问 答案显而易见

    您需要使用传递所需的参数

    connect_args=
    
    以及自sqlalchemy以来的参数

    所以你的python代码和他的很像

    from sqlalchemy import create_engine
    import pymysql
    from pymysql.constants.CLIENT import MULTI_STATEMENTS
    user = 'root'
    mysql_pass = 'testpassword'
    mysql_ip = 'localhost'
    cmd = 'SELECT * FROM table1;SELECT * FROM test'
    
    engine = create_engine("mysql+pymysql://{}:{}@{}:3306/testdb1?charset=utf8".format(user,mysql_pass,mysql_ip),connect_args={"client_flag": MULTI_STATEMENTS})
    connection = engine.raw_connection()
    
    try:
        cursor = connection.cursor()
        cursor.execute(cmd)
        results_one = cursor.fetchall()
        cursor.nextset()
        results_two = cursor.fetchall()
        cursor.close()
    finally:
        connection.close()
    
    但使用此解决方案,您需要事先知道运行哪些查询

    如果您想更加灵活,使用动态sql语句

    from sqlalchemy import create_engine
    user = 'root'
    mysql_pass = 'testpassword'
    mysql_ip = 'localhost'
    cmd = 'SELECT * FROM table1;SELECT * FROM test'
    engine = create_engine("mysql+pymysql://{}:{}@{}:3306/testdb1?charset=utf8".format(user,mysql_pass,mysql_ip))
    connection = engine.raw_connection()
    splitstring = cmd.split(";")
    ges_resultset = []
    try:
        cursor = connection.cursor()
        for cmdoneonly in splitstring:
            cursor.execute(cmdoneonly)
            results = cursor.fetchall()
            ges_resultset.append(results) 
        cursor.close()
    finally:
        connection.close()
    
    在这里,您可以检查每个命令,并了解python如何对其作出反应

    • 选择需要获取结果集
    • 插入删除创建您不需要的内容(还有更多内容,但您可以了解要点)

    @Gord Thompson,我对自动设置num_尝试进行了一些改进:

    import pandas as pd
    import pymysql
    from pymysql.constants import CLIENT
    
    conn_info = {
        "host": "localhost",
        "port": 3306,
        "user": "root",
        "password": "your mysql passwd",
        "client_flag": CLIENT.MULTI_STATEMENTS,
    }
    
    cnxn = pymysql.connect(**conn_info)
    crsr = cnxn.cursor()
    
    sql = """\
    create database sample;
    USE sample;
    CREATE TEMPORARY TABLE tmp (id int primary key, txt varchar(20)) 
        ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
    INSERT INTO tmp (id, txt) VALUES (1, 'foo'), (2, 'ΟΠΑ!');
    SELECT id, txt FROM tmp;
    SELECT txt FROM tmp;
    """
    
    crsr.execute(sql)
    num_tries = sql.count(';') if sql.endswith(';') else sql.count(';') + 1
    
    
    for i in range(num_tries):
        result = crsr.fetchall()
        if result:
            df = pd.DataFrame(result, columns=[x[0] for x in crsr.description])
            print(df)
        crsr.nextset()
    
    @nbk:当
    cmd
    包含许多sql语句时,执行代码可能会遇到以下问题:

    pymysql.err.InternalError: (1065, 'Query was empty')
    
    根据您的代码进行一些改进:

    import pandas as pd
    from sqlalchemy import create_engine
    user = 'root'
    mysql_pass = 'your mysql passwd'
    mysql_ip = 'localhost'
    
    sql = """\
    create database sample;
    USE sample;
    CREATE TEMPORARY TABLE tmp (id int primary key, txt varchar(20)) 
        ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
    INSERT INTO tmp (id, txt) VALUES (1, 'foo'), (2, 'ΟΠΑ!');
    SELECT id, txt FROM tmp;
    SELECT txt FROM tmp;
    """
    
    engine = create_engine("mysql+pymysql://{}:{}@{}:3306".format(user,mysql_pass,mysql_ip))
    
    connection = engine.raw_connection()
    
    splitstring = sql.split(";")
    try:
        cursor = connection.cursor()
        for cmdoneonly in splitstring:
            if cmdoneonly.strip():
                cursor.execute(cmdoneonly)
                results = cursor.fetchall()
                if results :
                    df = pd.DataFrame(results, columns=[x[0] for x in cursor.description])
                    print(df)
        cursor.close()
    finally:
        connection.close()
    
    • 如果cmdoneonly.strip():
    为避免
    1065:查询为空
    错误,则需要添加一个determine语句

  • 这是一个很棒的语句
    df=pd.DataFrame(结果,cursor.description中x的列=[x[0])
    学习
    @Gord Thompson


  • 如果未编辑收到的错误消息,则cmd变量(或文本文件)显然包含“cmd commands here”字符串。如果您编辑了它,在不知道sql错误是什么的情况下很难帮助您。太棒了
    df=pd.DataFrame(结果,列=[x[0]表示crsr.description中的x])
    ,卓越!希望SQLAlchemy上的开发人员升级以支持MULTI_语句的sql。我如何才能向您发送250声誉奖金?@showkey-我很感激您的想法,但不用担心。很高兴您找到了一个令人满意的解决方案。请参阅re:在分号字符上拆分匿名代码块。这是一个关于mysql和pymysql的问题,正如预期的那样,对于SQL server,我根本找不到任何支持SQL server多查询的解决方案。但请随意告知,默认情况下,meSQL服务器支持匿名代码块(至少在我遇到的任何情况下),因此无需显式启用“多个语句”。该启用特定于py、ysql,并且因驱动程序而异。我怀疑我们是否可以制作一个通用的特别代码,但这将是一个离题的话题。我如何才能向您发送250个声誉奖金?请注意,一些SQL方言(例如T-SQL)可能会非常宽松地要求语句以分号结尾,因此简单地将文本拆分为计数语句并不总是有效的。
    import pandas as pd
    import pymysql
    from pymysql.constants import CLIENT
    
    conn_info = {
        "host": "localhost",
        "port": 3306,
        "user": "root",
        "password": "your mysql passwd",
        "client_flag": CLIENT.MULTI_STATEMENTS,
    }
    
    cnxn = pymysql.connect(**conn_info)
    crsr = cnxn.cursor()
    
    sql = """\
    create database sample;
    USE sample;
    CREATE TEMPORARY TABLE tmp (id int primary key, txt varchar(20)) 
        ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
    INSERT INTO tmp (id, txt) VALUES (1, 'foo'), (2, 'ΟΠΑ!');
    SELECT id, txt FROM tmp;
    SELECT txt FROM tmp;
    """
    
    crsr.execute(sql)
    num_tries = sql.count(';') if sql.endswith(';') else sql.count(';') + 1
    
    
    for i in range(num_tries):
        result = crsr.fetchall()
        if result:
            df = pd.DataFrame(result, columns=[x[0] for x in crsr.description])
            print(df)
        crsr.nextset()
    
    pymysql.err.InternalError: (1065, 'Query was empty')
    
    import pandas as pd
    from sqlalchemy import create_engine
    user = 'root'
    mysql_pass = 'your mysql passwd'
    mysql_ip = 'localhost'
    
    sql = """\
    create database sample;
    USE sample;
    CREATE TEMPORARY TABLE tmp (id int primary key, txt varchar(20)) 
        ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
    INSERT INTO tmp (id, txt) VALUES (1, 'foo'), (2, 'ΟΠΑ!');
    SELECT id, txt FROM tmp;
    SELECT txt FROM tmp;
    """
    
    engine = create_engine("mysql+pymysql://{}:{}@{}:3306".format(user,mysql_pass,mysql_ip))
    
    connection = engine.raw_connection()
    
    splitstring = sql.split(";")
    try:
        cursor = connection.cursor()
        for cmdoneonly in splitstring:
            if cmdoneonly.strip():
                cursor.execute(cmdoneonly)
                results = cursor.fetchall()
                if results :
                    df = pd.DataFrame(results, columns=[x[0] for x in cursor.description])
                    print(df)
        cursor.close()
    finally:
        connection.close()