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 中设置,可能的值为 TERA
、ANSI
和 DEFAULT
。
差异之一是文字的大小写敏感度,在 Teradata 会话中它不区分大小写,而 ANSI 会话默认区分大小写,这解释了 WHEN MECHANIC = 'JOHN' THEN 1
的匹配数量不同时有 JOHN
和 John
.
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 手册中的章节。
我公司有一些 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 中设置,可能的值为 TERA
、ANSI
和 DEFAULT
。
差异之一是文字的大小写敏感度,在 Teradata 会话中它不区分大小写,而 ANSI 会话默认区分大小写,这解释了 WHEN MECHANIC = 'JOHN' THEN 1
的匹配数量不同时有 JOHN
和 John
.
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 手册中的章节。