Jak przekonwertować wynik zapytania SQL na strukturę danych PANDAS?

116

Każda pomoc w tym problemie będzie bardzo mile widziana.

Zasadniczo chcę uruchomić zapytanie do mojej bazy danych SQL i zapisać zwrócone dane jako strukturę danych Pandas.

Załączam kod do zapytania.

Czytam dokumentację dotyczącą Pand, ale mam problem ze zidentyfikowaniem typu zwracanego zapytania.

Próbowałem wydrukować wynik zapytania, ale nie zawiera on żadnych przydatnych informacji.

Dzięki!!!!

from sqlalchemy import create_engine

engine2 = create_engine('mysql://THE DATABASE I AM ACCESSING')
connection2 = engine2.connect()
dataid = 1022
resoverall = connection2.execute("
  SELECT 
      sum(BLABLA) AS BLA,
      sum(BLABLABLA2) AS BLABLABLA2,
      sum(SOME_INT) AS SOME_INT,
      sum(SOME_INT2) AS SOME_INT2,
      100*sum(SOME_INT2)/sum(SOME_INT) AS ctr,
      sum(SOME_INT2)/sum(SOME_INT) AS cpc
   FROM daily_report_cooked
   WHERE campaign_id = '%s'", %dataid)

W pewnym sensie chcę zrozumieć, jaki jest format / typ danych mojej zmiennej „resoverall” i jak umieścić ją w strukturze danych PANDAS.

user1613017
źródło
Zasadniczo, jaka jest struktura / typ zmiennej „resoverall” i jak przekonwertować ją na strukturę danych Pandas.
user1613017
Pandy brzmią dość interesująco, wcześniej o tym nie słyszałem, ale to pytanie nie ma żadnego sensu. Czy możesz spróbować wyjaśnić, co masz na myśli, mówiąc, że „nie podaje żadnych przydatnych informacji”?
tadman,
1
Ponieważ zapytanie, które wykonałem, zwraca wynik, po prostu zastanawiam się, jak manipulować tym zwrotem i przekształcić go w strukturę danych pandy. Jestem bardzo nowy w Pythonie i dlatego nie mam zbyt dużej wiedzy, na przykład to, co robimy w PHP, to po prostu zrobienie tablicy sql_fetch_array i mamy "użyteczne" dane. =)
user1613017

Odpowiedzi:

120

Oto najkrótszy kod, który wykona zadanie:

from pandas import DataFrame
df = DataFrame(resoverall.fetchall())
df.columns = resoverall.keys()

Możesz być bardziej wyszukany i przeanalizować typy, jak w odpowiedzi Paula.

Daniel Velkov
źródło
1
To zadziałało dla mnie dla 1.000.000 rekordów pobranych z bazy danych Oracle.
Erdem KAYA,
8
df = DataFrame(cursor.fetchall())zwraca ValueError: DataFrame constructor not properly called!, wydaje się, że krotka krotek nie jest akceptowalna dla konstruktora DataFrame. Nie ma również .keys()kursora ani w trybie słownika, ani w trybie krotki.
Mobigital
3
Zwróć uwagę, że metoda keys będzie działać tylko z wynikami uzyskanymi przy użyciu sqlalchemy. Pyodbc używa atrybutu opisu dla kolumn.
Filip
Czy to działa w przypadku baz danych Postgres? Próbuję uzyskać nazwy kolumn dla wynikowej ramki danych z keys()funkcją, ale nie mogę jej uruchomić.
Bowen Liu
1
@BowenLiu Tak, możesz używać z psycopg2df.columns=[ x.name for x in recoverall.description ]
Gnudiff
137

Edycja: marzec 2015

Jak zauważono poniżej, pandy używają teraz SQLAlchemy zarówno do odczytu z ( read_sql ), jak i wstawienia do ( to_sql ) bazy danych. Poniższe powinny działać

import pandas as pd

df = pd.read_sql(sql, cnxn)

Poprzednia odpowiedź: Via mikebmassey z podobnego pytania

import pyodbc
import pandas.io.sql as psql

cnxn = pyodbc.connect(connection_info) 
cursor = cnxn.cursor()
sql = "SELECT * FROM TABLE"

df = psql.frame_query(sql, cnxn)
cnxn.close()
beardc
źródło
Wydaje się, że jest to najlepszy sposób, aby to zrobić, ponieważ nie musisz ręcznie używać .keys (), aby uzyskać indeks kolumny. Prawdopodobnie odpowiedź Daniela została napisana zanim ta metoda istniała. Możesz także użyć pandas.io.sql.read_frame ()
RobinL
1
@openwonk, gdzie można by zaimplementować pd.read_sql()w powyższym fragmencie kodu?
3kstc
Właściwie od czasu mojej ostatniej odpowiedzi używałem pyodbci pandasrazem całkiem sporo. Dodanie nowej odpowiedzi z przykładem FYI.
openwonk
33

Jeśli używasz ORM SQLAlchemy zamiast języka wyrażeń, możesz chcieć przekonwertować obiekt typu sqlalchemy.orm.query.Queryna ramkę danych Pandas.

Najczystszym podejściem jest pobranie wygenerowanego kodu SQL z atrybutu instrukcji zapytania, a następnie wykonanie go za pomocą read_sql()metody pandy . Np. Zaczynając od obiektu Query o nazwie query:

df = pd.read_sql(query.statement, query.session.bind)
Nathan Gould
źródło
5
Bardziej wydajnym podejściem jest pobranie instrukcji z sqlalchemy i pozwolenie pandom na samodzielne wykonanie zapytania pandas.read_sql_query, przekazując query.statementje do niego. Zobacz tę odpowiedź: stackoverflow.com/a/29528804/1273938
LeoRochael
Dzięki @LeoRochael! Zmieniłem odpowiedź. Zdecydowanie czystsze!
Nathan Gould,
23

Edycja 2014-09-30:

pandy mają teraz read_sqlfunkcję. Na pewno chcesz tego użyć.

Oryginalna odpowiedź:

Nie mogę Ci pomóc z SQLAlchemy - zawsze używam pyodbc, MySQLdb lub psychopg2 w razie potrzeby. Ale kiedy to robię, funkcja tak prosta jak ta poniżej zwykle odpowiada moim potrzebom:

import decimal

import pydobc
import numpy as np
import pandas

cnn, cur = myConnectToDBfunction()
cmd = "SELECT * FROM myTable"
cur.execute(cmd)
dataframe = __processCursor(cur, dataframe=True)

def __processCursor(cur, dataframe=False, index=None):
    '''
    Processes a database cursor with data on it into either
    a structured numpy array or a pandas dataframe.

    input:
    cur - a pyodbc cursor that has just received data
    dataframe - bool. if false, a numpy record array is returned
                if true, return a pandas dataframe
    index - list of column(s) to use as index in a pandas dataframe
    '''
    datatypes = []
    colinfo = cur.description
    for col in colinfo:
        if col[1] == unicode:
            datatypes.append((col[0], 'U%d' % col[3]))
        elif col[1] == str:
            datatypes.append((col[0], 'S%d' % col[3]))
        elif col[1] in [float, decimal.Decimal]:
            datatypes.append((col[0], 'f4'))
        elif col[1] == datetime.datetime:
            datatypes.append((col[0], 'O4'))
        elif col[1] == int:
            datatypes.append((col[0], 'i4'))

    data = []
    for row in cur:
        data.append(tuple(row))

    array = np.array(data, dtype=datatypes)
    if dataframe:
        output = pandas.DataFrame.from_records(array)

        if index is not None:
            output = output.set_index(index)

    else:
        output = array

    return output
Paul H.
źródło
Myślę, że gdzieś u góry musisz zaimportować dziesiętne?
joefromct
@joefromct Być może, ale ta odpowiedź jest tak przestarzała, że ​​powinienem po prostu wyjaśnić całą sprawę i pokazać metody pandy.
Paul H
Może to mieć znaczenie dla niektórych ... powodem, dla którego to studiowałem, był mój inny problem, używając read_sql () tutaj stackoverflow.com/questions/32847246/ ...
joefromct
Jest to istotne dla tych, którzy nie mogą używać SQLAlchemy, która nie obsługuje wszystkich baz danych.
lamekul
@lamecicle nieco się nie zgadzam. IIRC, read_sqlnadal może akceptować połączenia inne niż SQLAlchemy przez np. Pyodbc, psychopg2 itd.
Paul H
16

Złącze MySQL

Dla tych, które współpracują ze złączem mysql, możesz użyć tego kodu jako początku. (Dzięki @Daniel Velkov)

Używane referencje:


import pandas as pd
import mysql.connector

# Setup MySQL connection
db = mysql.connector.connect(
    host="<IP>",              # your host, usually localhost
    user="<USER>",            # your username
    password="<PASS>",        # your password
    database="<DATABASE>"     # name of the data base
)   

# You must create a Cursor object. It will let you execute all the queries you need
cur = db.cursor()

# Use all the SQL you like
cur.execute("SELECT * FROM <TABLE>")

# Put it all to a data frame
sql_data = pd.DataFrame(cur.fetchall())
sql_data.columns = cur.column_names

# Close the session
db.close()

# Show the data
print(sql_data.head())
Thomas Devoogdt
źródło
9

Oto kod, którego używam. Mam nadzieję że to pomoże.

import pandas as pd
from sqlalchemy import create_engine

def getData():
  # Parameters
  ServerName = "my_server"
  Database = "my_db"
  UserPwd = "user:pwd"
  Driver = "driver=SQL Server Native Client 11.0"

  # Create the connection
  engine = create_engine('mssql+pyodbc://' + UserPwd + '@' + ServerName + '/' + Database + "?" + Driver)

  sql = "select * from mytable"
  df = pd.read_sql(sql, engine)
  return df

df2 = getData()
print(df2)
Murali Bala
źródło
9

Oto krótka i wyraźna odpowiedź na Twój problem:

from __future__ import print_function
import MySQLdb
import numpy as np
import pandas as pd
import xlrd

# Connecting to MySQL Database
connection = MySQLdb.connect(
             host="hostname",
             port=0000,
             user="userID",
             passwd="password",
             db="table_documents",
             charset='utf8'
           )
print(connection)
#getting data from database into a dataframe
sql_for_df = 'select * from tabledata'
df_from_database = pd.read_sql(sql_for_df , connection)
DeshDeep Singh
źródło
9

1. Korzystanie z MySQL-connector-python

# pip install mysql-connector-python

import mysql.connector
import pandas as pd

mydb = mysql.connector.connect(
    host = 'host',
    user = 'username',
    passwd = 'pass',
    database = 'db_name'
)
query = 'select * from table_name'
df = pd.read_sql(query, con = mydb)
print(df)

2. Korzystanie z SQLAlchemy

# pip install pymysql
# pip install sqlalchemy

import pandas as pd
import sqlalchemy

engine = sqlalchemy.create_engine('mysql+pymysql://username:password@localhost:3306/db_name')

query = '''
select * from table_name
'''
df = pd.read_sql_query(query, engine)
print(df)
Lintang Wisesa
źródło
prosta i świetna odpowiedź!
Lucas Aimaretto
5

Podobnie jak Nathan, często chcę zrzucić wyniki zapytania sqlalchemy lub sqlsoup Query do ramki danych Pandas. Moje własne rozwiązanie to:

query = session.query(tbl.Field1, tbl.Field2)
DataFrame(query.all(), columns=[column['name'] for column in query.column_descriptions])
Janak Mayer
źródło
1
Jeśli masz obiekt zapytania. Bardziej wydajne jest pobranie instrukcji z sqlalchemy i pozwolenie pandom na samodzielne wykonanie zapytania pandas.read_sql_query, przekazując query.statementje do niego. Zobacz tę odpowiedź: stackoverflow.com/a/29528804/1273938
LeoRochael
4

resoverallto obiekt ResultProxy sqlalchemy. Możesz przeczytać więcej na ten temat w dokumentacji sqlalchemy , która wyjaśnia podstawowe użycie pracy z silnikami i połączeniami. Ważne jest tutaj, że resoveralljest jak dykt.

Pandy lubi dyktować jak obiekty, aby tworzyć swoje struktury danych, zobacz dokumentację online

Powodzenia z sqlalchemy i pandami.

Wouter Overmeire
źródło
4

Po prostu użyj pandasi pyodbcrazem. Będziesz musiał zmodyfikować parametry połączenia ( connstr) zgodnie ze specyfikacjami bazy danych.

import pyodbc
import pandas as pd

# MSSQL Connection String Example
connstr = "Server=myServerAddress;Database=myDB;User Id=myUsername;Password=myPass;"

# Query Database and Create DataFrame Using Results
df = pd.read_sql("select * from myTable", pyodbc.connect(connstr))

Korzystałem pyodbcz kilku korporacyjnych baz danych (np. SQL Server, MySQL, MariaDB, IBM).

openwonk
źródło
Jak ponownie zapisać tę ramkę danych z powrotem do MSSQL przy użyciu Pyodbc? Inne niż używanie sqlalchemy
Ramsey
Użyj to_sqlmetody na DataFrameobiekcie. Ta metoda jest domyślnie SQLite, więc musisz jawnie przekazać jej obiekt wskazujący na bazę danych MSSQL. Zobacz dokumentację .
openwonk
Wypróbowałem poniższy i mam około 200 tysięcy wierszy z 13 kolumnami. Nie kończy się również po 15 minutach. Jakieś pomysły? df.to_sql ('tablename', engine, schema = 'schemaname', if_exists = 'append', index = False)
Ramsey
To wydaje się powolne ... Przepraszam, prawdopodobnie musiałbym zobaczyć cały kod w akcji. Chciałbym być pandasbardziej zoptymalizowany pod kątem lekkiej pracy w ETL, ale niestety ...
openwonk
3

To stare pytanie, ale chciałem dodać swoje dwa centy. Odczytuję pytanie jako „Chcę uruchomić zapytanie do mojej [mojej] bazy danych SQL i zapisać zwrócone dane jako strukturę danych Pandas [DataFrame]”.

Z kodu wygląda na to, że masz na myśli bazę danych mysql i zakładasz, że masz na myśli pandy DataFrame.

import MySQLdb as mdb
import pandas.io.sql as sql
from pandas import *

conn = mdb.connect('<server>','<user>','<pass>','<db>');
df = sql.read_frame('<query>', conn)

Na przykład,

conn = mdb.connect('localhost','myname','mypass','testdb');
df = sql.read_frame('select * from testTable', conn)

Spowoduje to zaimportowanie wszystkich wierszy tabeli testTable do ramki DataFrame.

BubbleGuppies
źródło
1

Tu jest moje. Na wszelki wypadek, jeśli używasz „pymysql”:

import pymysql
from pandas import DataFrame

host   = 'localhost'
port   = 3306
user   = 'yourUserName'
passwd = 'yourPassword'
db     = 'yourDatabase'

cnx    = pymysql.connect(host=host, port=port, user=user, passwd=passwd, db=db)
cur    = cnx.cursor()

query  = """ SELECT * FROM yourTable LIMIT 10"""
cur.execute(query)

field_names = [i[0] for i in cur.description]
get_data = [xx for xx in cur]

cur.close()
cnx.close()

df = DataFrame(get_data)
df.columns = field_names
kennyut
źródło
1

pandas.io.sql.write_frame jest DEPRECATED. https://pandas.pydata.org/pandas-docs/version/0.15.2/generated/pandas.io.sql.write_frame.html

Powinien zmienić się na pandas.DataFrame.to_sql https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_sql.html

Jest inne rozwiązanie. PYODBC na Pandy - DataFrame nie działa - Kształt przekazywanych wartości to (x, y), indeksy implikują (w, z)

Od wersji Pandas 0.12 (wierzę) możesz:

import pandas
import pyodbc

sql = 'select * from table'
cnn = pyodbc.connect(...)

data = pandas.read_sql(sql, cnn)

Przed wersją 0.12 można było:

import pandas
from pandas.io.sql import read_frame
import pyodbc

sql = 'select * from table'
cnn = pyodbc.connect(...)

data = read_frame(sql, cnn)
江明哲
źródło
To zdecydowanie najłatwiejszy sposób
Wilmer E. Henao
0

Dawno od ostatniego posta, ale może komuś to pomoże ...

Krótszy sposób niż Paul H:

my_dic = session.query(query.all())
my_df = pandas.DataFrame.from_dict(my_dic)
Antonio Fernandez
źródło
0

najlepszy sposób, w jaki to robię

db.execute(query) where db=db_class() #database class
    mydata=[x for x in db.fetchall()]
    df=pd.DataFrame(data=mydata)
Berto
źródło
0

Jeśli typ wyniku to ResultSet , należy najpierw przekonwertować go na słownik. Następnie kolumny DataFrame zostaną zebrane automatycznie.

Działa to w moim przypadku:

df = pd.DataFrame([dict(r) for r in resoverall])
tanza9
źródło