sqlalchemy 和 Teradata Studio 从相同的语句中产生不同的结果

sqlalchemy and Teradata Studio yielding different results from the same statements

我公司有一些 table 需要根据查询定期更新。通常,我会将查询变成一个视图,但目标模式没有普遍存在的 select 访问权限,IT 部门通常需要一段时间才能在这方面帮助我们,我们需要 tables快速设置。

因此,我创建了一个 python 脚本来读取 .sql 文件,将其解析为通常涉及创建易变 table 的单独语句,然后执行这些语句通过sql炼金术。最后,python 程序生成一个 MERGE 语句,以从 .sql 文件中的最终易失性 table 更新目标 table。 python 看起来像这样:

import sys
import re
import time
from os.path import dirname

import sqlalchemy
import sqlalchemy_teradata
from my_stuff import CSpinner, end_program, Nudger, get_auth, create_update_text

t0 = time.clock()

# Assigning Variables
print('Assigning Variables')
user, pw, connstring, schema = get_auth()
td_engine = sqlalchemy.create_engine(f'teradata://{user}:{pw}@{connstring}/{schema}')

if __name__ == '__main__':
    initial_sql_file = rf'{dirname(__file__)}\{sys.argv[1]}'
    table_name = sys.argv[2]
    keys = eval(sys.argv[3])

    # Reading the SQL file to get the result set
    with open(initial_sql_file, 'r') as f:
        sql_raw = f.read()

    statements = re.findall(r'.+?;', sql_raw, re.S)
    cols = td_engine.execute(f'SEL TOP 1* from {table_name}').keys()
    print(f'Assigned\n\n{table_name}')

    # Querying TD using the SQL read above
    with CSpinner('\nPerforming SQL Magic...', '...Performed'):
        with td_engine.connect() as conn:
            n = 0
            for sttmnt in statements:
                conn.execute(sttmnt)
                n += 1
                print(f'Statement {n} Complete')
            merge_sql = create_update_text('_data', table_name, cols, keys)
            print(merge_sql)
            conn.execute(merge_sql)
    time.sleep(.1)

    # ends the program
    end_program(t0)

鉴于我之前有 ,我有 create_update_text return sqlalchemy.text(statement).execution_options(autocommit=True) 如下所示:

def create_update_text(temp, perm, df, keys, how='both'):
    cols = [col for col in df]

    for key in keys:
        assert key in cols, f'ID {key} not in Columns'

    joined_keys = ' AND '.join(f'p."{key}" = t."{key}"' for key in keys)
    sql_text = f'MERGE INTO {perm} p\n' \
               f'USING {temp} t\n' \
               f'ON {joined_keys}\n'

    if how in {'both', 'matched'}:
        sql_text += 'WHEN MATCHED THEN\n' \
                    ' UPDATE\n' \
                    ' SET\n     '

        sql_text += ',\n     '.join(f'"{col}" = t."{col}"' for col in cols if col not in keys)

    if how in {'both', 'not'}:
        sql_text += '\nWHEN NOT MATCHED THEN\n' \
                    ' INSERT (\n     '

        sql_text += ',\n     '.join(f'"{col}"' for col in cols)

        sql_text += '\n )\n' \
                    ' VALUES (\n     '

        sql_text += ',\n     '.join(f't."{col}"' for col in cols)

        sql_text += '\n )'

    return sqlalchemy.text(sql_text).execution_options(autocommit=True)

一个简短的例子。sql 文件看起来像:

CREATE VOLATILE TABLE _data as (
    SEL
     PART_ID,
     REMOVAL_DATE,
     SUM(FLAGS) AS FLAG_SUM,

     CASE
      WHEN MECHANIC = 'JOHN' THEN 1
      ELSE 0
     END AS JOHNORNOT

    FROM SCHEMA.TABLE

    GROUP BY 1,2,4
) WITH DATA
  PRIMARY KEY (PART_ID,REMOVAL_DATE,JOHNORNOT)
  ON COMMIT PRESERVE ROWS
;

我 运行 遇到一个问题,其中 运行 使用 python 提交语句进行更新与粘贴查询和 python 相比会产生不同的结果- 将 MERGE 语句生成到 Teradata Studio 中,并将它们 运行 合并为单独的语句。例如,SUM 字段可能会产生不同的值,或者二进制 CASE 语句不会执行相同的值。

问题是,如果我 运行在 TD Studio 中使用与使用 SQLAlchemy 时完全相同的查询,为什么会产生不同的结果?我的程序是否有影响数据的地方?

为了简洁地进行比较,我将 TD 和 PY 数据放在单独的 table 中,运行 合并了两者的查询如下所示:

Select
 td.PART_ID
 td.MONTH
 td.YEAR
 td.REMOVALS as TD_Removals
 py.REMOVALS as PY_Removals
 td.FAILURES as TD_Failures
 py.FAILURES as PY_Failues

from TD_Data td

join PY_Data py
  on td.PART_ID = py.PART_ID
 and td.MONTH = py.MONTH
 and td.YEAR = py.YEAR
 and (td.REMOVALS <> py.REMOVALS
      or td.FAILURES <> py.FAILURES)

据我所知,不匹配的 PY_ 数字总是低于相应的 TD_ 值。部分示例数据如下所示:

PART_ID        MONTH YEAR TD_Removals PY_Removals TD_Fails PY_Fails
26-3132-9-0005 7     2015 2           1           0        0
26-2350-9-0001 3     2015 15          12          11       11
43-3614-9-0002 1     2017 2           0           0        0
97-2373-9-0001 3     2016 8           2           1        1
26-7410-9-0001 7     2016 6           1           0        0
26-3155-9-0003 9     2015 1           0           0        0
97-3510-9-0001 7     2017 28          26          0        0
97-2792-9-0006 6     2017 3           2           0        0
26-7933-9-0001 10    2015 3           0           0        0
97-2313-9-0002 3     2016 15          14          13       13
29-2800-9-0009 6     2017 3           2           0        0
26-3242-9-0006 7     2016 7           0           0        0

根据@dnoeth 在下面的评论,我在 python 和 TD_Studio 上 运行 SELECT Transaction_Mode FROM dbc.sessioninfoV WHERE SessionNo = SESSION;。 TD_Studio 产生 A,python 产生 T。现在的问题变成了哪个更正确,以后如何保证相似性。

根据评论,来自 Python/Studio 的会话使用不同的事务模式:

 SELECT Transaction_Mode -- T=Teradata, A=ANSI mode
 FROM dbc.sessioninfoV
 WHERE SessionNo = SESSION;

会话模式通过 TMODE 属性 在 JDBC 中设置,可能的值为 TERAANSIDEFAULT

差异之一是文字的大小写敏感度,在 Teradata 会话中它不区分大小写,而 ANSI 会话默认区分大小写,这解释了 WHEN MECHANIC = 'JOHN' THEN 1 的匹配数量不同时有 JOHNJohn.

Comparison of Transactions in ANSI and Teradata Session Modes

当列在 CREATE TABLE 中定义为 NOT CASESPECIFIC 时,您必须切换到 Teradata 模式或在每个字符串文字后添加 (NOT CASESPECIFIC),例如WHEN MECHANIC = 'JOHN' (NOT CASESPECIFIC) THEN 1.

有关 Teradata 和 ANSI 会话之间的所有差异,请参阅 Transaction Processing 手册中的章节。