尚不支持pandas + pyodbc ODBC SQL类型-150

Jon*_*ito 3 sql-server odbc pyodbc pandas

我知道有很多话题,但是我认为这很具体。我得到用于审计目的的当前代码:

import pandas as pd
import pyodbc

query = """
--Top 50 high total CPU Queries
SELECT TOP 50
'High CPU Queries' as Type,
serverproperty('machinename') as 'Server Name',
isnull(serverproperty('instancename'),serverproperty('machinename')) as 'Instance Name',
        COALESCE(DB_NAME(qt.dbid),
        DB_NAME(CAST(pa.value as int)), 
        'Resource') AS DBNAME,
    qs.execution_count as [Execution Count],
    qs.total_worker_time/1000 as [Total CPU Time],
    (qs.total_worker_time/1000)/qs.execution_count as [Avg CPU Time],
    qs.total_elapsed_time/1000 as [Total Duration],
    (qs.total_elapsed_time/1000)/qs.execution_count as [Avg Duration],
    qs.total_physical_reads as [Total Physical Reads],
    qs.total_physical_reads/qs.execution_count as [Avg Physical Reads],
    qs.total_logical_reads as [Total Logical Reads],
    qs.total_logical_reads/qs.execution_count as [Avg Logical Reads],
SUBSTRING(qt.text,qs.statement_start_offset/2, 
        (case when qs.statement_end_offset = -1 
        then len(convert(nvarchar(max), qt.text)) * 2 
        else qs.statement_end_offset end -qs.statement_start_offset)/2)
        as query_text     
    FROM sys.dm_exec_query_stats qs
    cross apply sys.dm_exec_sql_text(qs.sql_handle) as qt
    outer apply sys.dm_exec_query_plan (qs.plan_handle) qp
    outer APPLY sys.dm_exec_plan_attributes(qs.plan_handle) pa
    where attribute = 'dbid'   
    ORDER BY 
        [Total CPU Time] DESC
"""
cnxn = pyodbc.connect('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
cnxn.execute(query).fetchall()
cnxn.close()
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

cnxn.execute(sql_status_20).fetchall()追溯(最近一次调用为上):pyodbc.ProgrammingError中的文件“”,第1行,((尚不支持ODBC SQL类型-150。column-index = 1 type =- 150','HY106')

有人可以帮我解决这个问题吗?我对使用日期的许多SQL Server审核脚本有相同的问题,因为我的生产环境中有各种SQL版本,所以我无法更改驱动程序。

Gor*_*son 7

If it would be difficult to change the existing queries to explicitly CAST or CONVERT the troublesome values then you might consider trying to use a pyodbc Output Converter Function. It enables you to define a Python function that will be applied to the raw bytes returned for a given ODBC SQL type.

For example, this test code fails with the error you describe:

import pyodbc

cnxn = pyodbc.connect('DSN=SQLmyDb', autocommit=True)
crsr = cnxn.cursor()

server_name = crsr.execute("SELECT SERVERPROPERTY('machinename')").fetchval()
print(server_name)

crsr.close()
cnxn.close()
Run Code Online (Sandbox Code Playgroud)

but this works correctly for me under Python3

import pyodbc


def handle_sql_variant_as_string(value):
    return value.decode('utf-16le')


cnxn = pyodbc.connect('DSN=SQLmyDb', autocommit=True)
crsr = cnxn.cursor()

cnxn.add_output_converter(-150, handle_sql_variant_as_string)
server_name = crsr.execute("SELECT SERVERPROPERTY('machinename')").fetchval()
print(server_name)

crsr.close()
cnxn.close()
Run Code Online (Sandbox Code Playgroud)


Dan*_*man 6

如果无法更改驱动程序,则需要更改查询以返回其支持的数据类型。

SQL类型-150是SQL_VARIANT,由返回SERVERPROPERTY。解决方法是CAST将列显式指定为受支持的类型,例如nvarchar

CAST(SERVERPROPERTY('machinename') AS nvarchar(100)) AS 'Server Name',
CAST(ISNULL(SERVERPROPERTY('instancename'),SERVERPROPERTY('machinename')) AS nvarchar(100)) AS 'Instance Name',
Run Code Online (Sandbox Code Playgroud)