7
votes

SQLAlchemy accédant aux types de colonnes à partir des résultats de requête

Je me connecte à une base de données SQL Server en utilisant SQLAlchemy (avec le pilote pymssql ).

(('col_1', 1, None, None, None, None, None),
 ('col_2', 1, None, None, None, None, None),
 ('col_3', 4, None, None, None, None, None),
 ('col_4', 3, None, None, None, None, None),
 ('col_5', 3, None, None, None, None, None))

ce qui se traduit par:

import sqlalchemy

conn_string = f'mssql+pymssql://{uid}:{pwd}@{instance}/?database={db};charset=utf8'
sql = 'SELECT * FROM FAKETABLE;'

engine = sqlalchemy.create_engine(conn_string)
connection = engine.connect()
result = connection.execute(sql)
result.cursor.description

Selon PEP 249 (attribut .description du curseur):

Les deux premiers éléments (nom et type_code) sont obligatoires, les cinq autres sont facultatifs et sont définis sur Aucun si aucune valeur significative ne peut être fournie.

Je suppose que les entiers (1, 1, 4, 3, 3) sont des types de colonnes.

Mes deux questions:

  1. Comment mapper ces entiers à des types de données (comme char, integer, etc.)?
  2. S'agit-il de types de données SQL? Si non, est-il possible d'obtenir les types de données SQL?

FWIW, j'obtiens le même résultat en utilisant raw_connection() au lieu de connect() .

Je suis tombé sur trois questions dans le même sens (qui ne répondent pas à cette question spécifique). J'ai besoin d'utiliser l'approche connect() + execute() .


0 commentaires

3 Réponses :


3
votes

Si non, est-il possible d'obtenir les types de données SQL?

La fonction SQL Server sys.dm_exec_describe_first_result_set peut être utilisée pour obtenir le type de données de la colonne SQL directement pour la requête fournie:

CREATE TABLE FAKETABLE(id INT, d DATE, country NVARCHAR(10));

SELECT column_ordinal, name, system_type_name 
FROM sys.dm_exec_describe_first_result_set('SELECT * FROM FAKETABLE', NULL, 0) ;

+-----------------+----------+------------------+
| column_ordinal  |  name    | system_type_name |
+-----------------+----------+------------------+
|              1  | id       | int              |
|              2  | d        | date             |
|              3  | country  | nvarchar(10)     |
+-----------------+----------+------------------+

Dans votre exemple:

sql = """SELECT column_ordinal, name, system_type_name 
    FROM sys.dm_exec_describe_first_result_set('SELECT * FROM FAKETABLE', NULL, 0) ;"""

Pour:

SELECT column_ordinal, name, system_type_name, *
FROM sys.dm_exec_describe_first_result_set('here goes query', NULL, 0) ; 

démo db <> violon


1 commentaires

Merci, j'espérais que la réponse serait disponible quelque part dans les métadonnées, mais c'est le meilleur que j'ai.



1
votes

En regardant PEP249 : type_code ne semble pas être le même à travers un type de base de données différent.

Cette réponse se concentrera donc sur MS SQL Server.

  1. Comment mapper ces entiers à des types de données (comme char, integer, etc.)?

Vous pouvez créer un dict de type_code dans type_object à l'aide du code suivant:

{2: ('BINARY', <DBAPIType 2>),
 4: ('DATETIME', <DBAPIType 4>),
 5: ('DECIMAL', <DBAPIType 5>),
 3: ('NUMBER', <DBAPIType 3>),
 1: ('STRING', <DBAPIType 1>)}

Cela produira le dict suivant:

import inspect
import pymssql

code_map = {
    type_obj.value: (type_name, type_obj)
    for type_name, type_obj
    in inspect.getmembers(
        pymssql,
        predicate=lambda x: isinstance(x, pymssql.DBAPIType),
    )
}

Malheureusement, je n'ai pas accès à une instance en cours d'exécution de MS SQL Server. Je ne suis donc pas en mesure de vérifier si les résultats de type correspondent à votre exemple.

  1. S'agit-il de types de données SQL? Si non, est-il possible d'obtenir les types de données SQL?

En regardant le PEP et ce résultat: ces champs ne sont pas des types de données SQL. Il s'agit de "Type Object".

L'API DB n'a pas l'air de fournir des méthodes / fonctions pour inspecter les métadonnées des résultats de la requête. L'API fournit simplement un moyen de lier les types de données de SQL aux types python.

Si vous avez besoin d'obtenir le type de données SQL exact, vous devez écrire une requête SQL spécifique au serveur.


1 commentaires

Merci, mais il semble que ce ne soit que le mappage du code au type de données. Je suis particulièrement intéressé par les types de données SQL.



1
votes

Ce pourrait être le pilote lui-même. Ci-dessous, j'ai presque le même code que le vôtre, en utilisant simplement le pilote pyodbc sur AdventureWorks. J'ai choisi une table avec beaucoup de types de données différents et ils sont tous affichés.

(('ProductID', <class 'int'>, None, 10, 10, 0, False), ('Name', <class 'str'>, None, 50, 50, 0, False), ('ProductNumber', <class 'str'>, None, 25, 25, 0, False), ('Color', <class 'str'>, None, 15, 15, 0, True), ('StandardCost', <class 'decimal.Decimal'>, None, 19, 19, 4, False), ('ListPrice', <class 'decimal.Decimal'>, None, 19, 19, 4, False), ('Size', <class 'str'>, None, 5, 5, 0, True), ('Weight', <class 'decimal.Decimal'>, None, 8, 8, 2, True), ('ProductCategoryID', <class 'int'>, None, 10, 10, 0, True), ('ProductModelID', <class 'int'>, None, 10, 10, 0, True), ('SellStartDate', <class 'datetime.datetime'>, None, 23, 23, 3, False), ('SellEndDate', <class 'datetime.datetime'>, None, 23, 23, 3, True), ('DiscontinuedDate', <class 'datetime.datetime'>, None, 23, 23, 3, True), ('ThumbNailPhoto', <class 'bytearray'>, None, 0, 0, 0, True), ('ThumbnailPhotoFileName', <class 'str'>, None, 50, 50, 0, True), ('rowguid', <class 'str'>, None, 36, 36, 0, False), ('ModifiedDate', <class 'datetime.datetime'>, None, 23, 23, 3, False))

Production:

import sqlalchemy
conn_string = conn_string = f'mssql+pyodbc://{username}:{pwd}@{instance}/AdventureWorksLT2017?driver=ODBC+Driver+17+for+SQL+Server'
sql = 'SELECT TOP 10 * FROM SalesLT.Product;'

engine = sqlalchemy.create_engine(conn_string)
connection = engine.connect()

result = connection.execute(sql)
print(result.cursor.description)

Pouvez-vous essayer ce pilote à titre de comparaison?


1 commentaires

Merci, c'est utile. Mais j'ai besoin d'utiliser pymssql .