The Teradata database has different modes for running queries: ANSI Mode (rules based on the ANSI SQL: 2011 specifications) and TERA mode (rules defined by Teradata). Please review the following Teradata documentation for more information.
Teradata mode for string
For strings, the Teradata mode works differently. As it is explained in the following table based on the Teradata documentation:
Feature
ANSI mode
Teradata mode
Translation specification summary
For this feature, the intended translation is focused only on Tera Mode. Notice that ANSI mode is added here just for comparison.
Mode
Column constraint values
Teradata behavior
SnowConvert expected behavior
Sample Source Patterns
Setup data
CREATETABLEdatabaseTest.employees ( first_name VARCHAR(50) NOT CASESPECIFIC, last_name VARCHAR(50) CASESPECIFIC, department VARCHAR(50) CASESPECIFIC);INSERT INTO databaseTest.employees(first_name, last_name, department) VALUES ('George', 'Snow', 'Sales');INSERT INTO databaseTest.employees(first_name, last_name, department) VALUES ('Jonh', 'Snow', 'Engineering');
Sample 1: Column constraint is NOT CASESPECIFIC and database mode is TERA Mode
This example demonstrates the usage of a column set up as NOT CASE SPECIFIC as it is a first_name column. Even when asking for the string 'GEorge', the query execution will retrieve results in Teradata because the case specification is not considered.
To emulate this scenario in Snowflake, there are implemented two functions: RTRIM(UPPER(string_evaluation)), UPPER is required in this scenario because the string does not review the case specification.
Sample 2: Column constraint is CASESPECIFIC and database mode is TERA Mode
For this example, the column constraint is CASESPECIFIC, for which the example does not retrieve rows in Teradata because 'Snow' is not equal to 'SNOW'.
In Snowflake, the resulting migration points only to the use of the RTRIM function since the case specification is important.
Sample 2: Column constraint is CASESPECIFIC and database mode is TERA Mode
In this case, the case specification is important. The query is expected to show two rows in the results since there are two coincidences for the last name "Snow". If there is an upper case letter in the middle of the comparison, like "sNow', there would be zero rows in the result.
Sample 1: Column constraint is NOT CASESPECIFIC and database mode is TERA Mode
Teradata
SELECT first_nameFROM databaseTest.employeesGROUP BY first_nameHAVING first_name ='GeorgE';
Snowflake
SELECT first_nameFROM databaseTest.employeesGROUP BY first_nameHAVINGRTRIM(UPPER(first_name)) =RTRIM(UPPER('GeorgE'));
Sample 2: Column constraint is CASESPECIFIC and database mode is TERA Mode
Teradata
SELECT departmentFROM databaseTest.employeesGROUP BY departmentHAVING department ='Sales';
Snowflake
SELECT departmentFROM databaseTest.employeesGROUP BY departmentHAVINGRTRIM(department) =RTRIM('Sales');
IN Operation
Sample 1: Column constraint is NOT CASESPECIFIC and database mode is TERA Mode
Since this example is NOT CASESPECIFIC, there has to be the RTRIM and UPPER functions in the translation to Snowflake. So, the results are the same in the Teradata database and Snowflake database.
Teradata
SELECT*FROM databaseTest.employeesWHERE first_name IN ('GeorgE');
Snowflake
SELECT*FROM databaseTest.employeesWHERERTRIM(UPPER(first_name)) IN (RTRIM(UPPER('GeorgE')));
Sample 2: Column constraint is CASESPECIFIC and database mode is TERA Mode
For this example, the usage of the UPPER function is not required since, in the Teradata database, the case specification is relevant to the results. It is expected to not have rows in the results because "Sales" is not the same as "SaleS".
Teradata
SELECT*FROM databaseTest.employeesWHERE department IN ('SaleS');
Query produced no results.
Snowflake
SELECT*FROM databaseTest.employeesWHERERTRIM(department) IN (RTRIM('SaleS'));
To review this clauses, it is required to set up new and better delimited data to showcase the behavior of the statements.
Sample 1: Column constraint is NOT CASESPECIFIC and database mode is TERA Mode
Setup data
CREATETABLEdatabaseTest.students ( first_name VARCHAR(50) NOT CASESPECIFIC);INSERT INTO databaseTest.students(first_name) VALUES ('George');INSERT INTO databaseTest.students(first_name) VALUES (' George');
Notice that this sample does not allow to insert values with upper and lower case letters in the NOT CASE SPECIFIC column because it takes it as the same value. Because the column does not supervise the case specification, the 'GEORGE' and 'george' value are checked as the same information.
The following rows are taken as duplicated row error:
INSERT INTO databaseTest.students(first_name) VALUES ('GEORGE');INSERT INTO databaseTest.students(first_name) VALUES ('GeorGe');INSERT INTO databaseTest.students(first_name) VALUES ('George ');INSERT INTO databaseTest.students(first_name) VALUES ('GeOrge');INSERT INTO databaseTest.students(first_name) VALUES ('GEorge');INSERT INTO databaseTest.students(first_name) VALUES ('George');
Please review the insertion of data in Snowflake. Snowflake does allow the insertion of values as 'GEORGE' and 'georges' without showing errors because the case specification is not bound explicitly with the column.
Query sample
Notice that this output can differ. To ensure a functional equivalence, it is required to use the COLLATE expression.
Please review the SSC-EWI-TD0007 for more information.
Teradata
SELECT first_nameFROM databaseTest.studentsGROUP BY first_name;
Snowflake
SELECT departmentFROM databaseTest.workers!!!RESOLVE EWI!!! /*** SSC-EWI-TD0007 - GROUP BY IS NOT EQUIVALENT IN TERADATA MODE ***/!!!GROUP BY department;
Sample 2: Column constraint is CASESPECIFIC and database mode is TERA Mode
Setup data
CREATETABLEdatabaseTest.students ( first_name VARCHAR(50) CASESPECIFIC);INSERT INTO databaseTest.students(first_name) VALUES ('George');INSERT INTO databaseTest.students(first_name) VALUES (' George');INSERT INTO databaseTest.students(first_name) VALUES ('GEORGE');INSERT INTO databaseTest.students(first_name) VALUES ('GeorGe');INSERT INTO databaseTest.students(first_name) VALUES ('GeOrge');INSERT INTO databaseTest.students(first_name) VALUES ('GEorge');
CREATE OR REPLACETABLEdatabaseTest.students ( first_name VARCHAR(50))COMMENT = '{ "origin": "sf_sc", "name": "snowconvert", "version": { "major": 0, "minor": 0, "patch": "0" }, "attributes": { "component": "teradata", "convertedOn": "07/31/2024" }}'
;INSERT INTO databaseTest.students (first_name)VALUES ('George');INSERT INTO databaseTest.students (first_name)VALUES (' George');INSERT INTO databaseTest.students (first_name)VALUES ('GEORGE');INSERT INTO databaseTest.students (first_name)VALUES ('GeorGe');INSERT INTO databaseTest.students (first_name)VALUES ('GeOrge');INSERT INTO databaseTest.students (first_name)VALUES ('GEorge');
About the column behavior
For this example, the following value shows duplicated row error:
INSERT INTO databaseTest.students(first_name) VALUES ('George ');
It is because, the blank spaces at the right are trimed so the value is taken as one already inserted.
Query sample
Notice that this output can differ. To ensure a functional equivalence, it is required to use the COLLATE expression.
Please review the SSC-EWI-TD0007 for more information.
Teradata
SELECT first_nameFROM databaseTest.studentsGROUP BY first_name;
Snowflake
SELECT departmentFROM databaseTest.workers!!!RESOLVE EWI!!! /*** SSC-EWI-TD0007 - GROUP BY IS NOT EQUIVALENT IN TERADATA MODE ***/!!!GROUP BY department;
JOIN clause
Simple scenarios with evaluation operations are supported.
To review these clauses, new and better-delimited data must be set up to showcase the statements' behavior.
Sample 1: Column constraint is NOT CASESPECIFIC and database mode is TERA Mode
Setup data
CREATETABLEdatabaseTest.employees ( employee_id INTEGERNOT NULL, first_name VARCHAR(50) NOT CASESPECIFIC, last_name VARCHAR(50) NOT CASESPECIFIC, department VARCHAR(50) NOT CASESPECIFIC);INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (1, 'Name_A', 'Last_name_A', 'IT');
INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (2, 'Name_B', 'Last_name_B', 'HR');
INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (3, 'Name_C', 'Last_name_C', 'IT');
INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (4, 'Name_D', 'Last_name_D', 'Sales');
INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (5, 'Name_E', 'Last_name_E', 'Finance');
INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (6, 'Name_F', 'Last_name_F', 'IT');
INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (7, 'Name_G', 'Last_name_G', 'HR');
INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (8, 'Name_H', 'Last_name_H', 'Sales');
INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (9, 'Name_I', 'Last_name_I', 'IT');
INSERT INTO databaseTest.employees (employee_id, first_name, last_name, department) VALUES (10, 'Name_J', 'Last_name_J', 'Finance');
CREATETABLEdatabaseTest.departments ( department_id INTEGERNOT NULL, department_name VARCHAR(50) NOT CASESPECIFIC,locationVARCHAR(100) NOT CASESPECIFIC,PRIMARY KEY (department_id));INSERT INTO databaseTest.departments (department_id, department_name, location) VALUES (101, 'Information Technology', 'New York');
INSERT INTO databaseTest.departments (department_id, department_name, location) VALUES (102, 'Human Resources', 'Chicago');
INSERT INTO databaseTest.departments (department_id, department_name, location) VALUES (103, 'Sales', 'San Francisco');INSERT INTO databaseTest.departments (department_id, department_name, location) VALUES (104, 'Finance', 'Boston');
Sample 1: Column constraint is NOT CASESPECIFIC and database mode is TERA Mode
For this example, it is required to set up specific data to test the examples. The column that is being evaluated is the department column. Notice that with the NOT CASESPECIFIC constraint, it is possible to insert different data with different upper and lower cases.
The migrated code uses UPPER to emulate Teradata's NOT CASESPECIFIC behavior.
Setup data
CREATETABLEdatabaseTest.workers ( first_name VARCHAR(50) NOT CASESPECIFIC, last_name VARCHAR(50) CASESPECIFIC, department VARCHAR(50) NOT CASESPECIFIC);INSERT INTO databaseTest.workers(first_name, last_name, department) VALUES ('George', 'Snow', 'Sales ');INSERT INTO databaseTest.workers(first_name, last_name, department) VALUES ('George', 'Snow', ' Sales');INSERT INTO databaseTest.workers(first_name, last_name, department) VALUES ('George1', 'Snow', 'SALES');INSERT INTO databaseTest.workers(first_name, last_name, department) VALUES ('George2', 'Snow', 'SalEs');INSERT INTO databaseTest.workers(first_name, last_name, department) VALUES ('George3', 'Snow', 'sales');
For all the following queries, the result is the same.
SELECT first_name (NOT CASESPECIFIC) from databaseTest.workers;
SELECT first_name (CASESPECIFIC) from databaseTest.workers;
SELECT first_name from databaseTest.workers;
SELECT * FROM databaseTest.workers;
Snowflake
Column cast should be removed.
SELECT first_name from databaseTest.workers;
Sample 2: Column constraint is CASESPECIFIC and database mode is TERA Mode
The order in the output is different from Teradata but values are the same.
The insertion queries should accept data with upper and lower case as different values. The right white spaces are trimmed so, 'George ' and 'George ' are the same.
Setup data
CREATE TABLE databaseTest.workers (
first_name VARCHAR(50) CASESPECIFIC
);
INSERT INTO databaseTest.workers(first_name) VALUES ('George');
INSERT INTO databaseTest.workers(first_name) VALUES (' George');
INSERT INTO databaseTest.workers(first_name) VALUES ('GEorge');
INSERT INTO databaseTest.workers(first_name) VALUES ('GEORGE');
For all the following queries, the result is the same.
SELECT first_name (NOT CASESPECIFIC) from databaseTest.workers;
SELECT first_name (CASESPECIFIC) from databaseTest.workers;
SELECT first_name from databaseTest.workers;
SELECT * FROM databaseTest.workers;
Snowflake
Column cast should be removed.
SELECT first_name from databaseTest.workers;
CAST to Operations or Clauses [Exceptional Case]
Sample 1: Column constraint is NOT CASESPECIFIC and database mode is TERA Mode
Setup data
CREATE TABLE databaseTest.workers (
first_name VARCHAR(50) NOT CASESPECIFIC
);
INSERT INTO databaseTest.workers(first_name) VALUES ('George');
INSERT INTO databaseTest.workers(first_name) VALUES (' George');
SELECT *
FROM databaseTest.workers
WHERE first_name = '';
1 rows with blanck spaces.
Snowflake
SELECT *
FROM databaseTest.workers
WHERE RTRIM(first_name) = RTRIM('');
1 rows with blanck spaces.
Known Issues
there are some mode-specific SQL statement restrictions: BEGIN TRANSACTION, END TRANSACTION, COMMIT [WORK].
Data insertion may differ in Snowflake since the case specification is not bound to the column declaration.
GROUP BY may differ in order, but group the correct values.
ORDER BY behaves differently in Snowflake.
If a function has a TRIM() from the source code, this workaround will add the required functions to the source code. So, RTRIM will be applied to the TRIM() source function.
Related EWIs
SSC-EWI-TD0007: GROUP BY IS NOT EQUIVALENT IN TERADATA MODE
first_name
last_name
department
first_name
last_name
department
first_name
last_name
department
first_name
last_name
department
first_name
last_name
department
first_name
last_name
department
first_name
first_name
department
department
first_name
last_name
department
first_name
last_name
department
first_name
last_name
department_full_name
first_name
last_name
department_full_name
first_name
first_name
first_name
first_name
department
department
department
department
first_name
first_name
first_name
first_name
first_name
first_name
Default attribute for character comparisons
CASESPECIFIC
NOT CASESPECIFIC
Default TRIM behavior
TRIM(BOTH FROM)
TRIM(BOTH FROM)
ANSI Mode
CASESPECIFIC
CASESPECIFIC
No constraint added.
NOT CASESPECIFIC
CASESPECIFIC
Add COLLATE 'en-cs' in column definition.
Teradata Mode
CASESPECIFIC
CASESPECIFIC
In most cases, do not add COLLATE, and convert its usages of string comparison to RTRIM( expression )
NOT CASESPECIFIC
NOT CASESPECIFIC
In most cases, do not add COLLATE, and convert its usages of string comparison to RTRIM(UPPER( expression ))