<@U0A71G31CDV> CASE WHEN LENGTH(TRIM(ID_CARD_...
# questions-and-troubleshooting
l
@Rocky CASE WHEN LENGTH(TRIM(ID_CARD_NUMBER_OTHER)) IN (9, 12) AND TRIM(ID_CARD_NUMBER_OTHER) REGEXP '^[0-9]+$' -- ← replaces TRY_CAST AND TRIM(ID_CARD_NUMBER_OTHER) NOT IN ('123456789', '987654321') AND TRIM(ID_CARD_NUMBER_OTHER) NOT REGEXP '^([0-9])\1+$' THEN TRIM(ID_CARD_NUMBER_OTHER) ELSE NULL END AS ID_CARD_NUMBER_OTHER, SQL Error [1064] [42000]: Invalid re2 expression: ^([0-9])\1+$ backend [id=3731846] [ Invalid re2 expression: ^([0-9])\1+$ backend [id=3731846] [
r
The error occurs because StarRocks uses the RE2 regular expression engine, which does not support backreferences (like
\1
). RE2 is designed for linear-time matching, and backreferences are excluded because they can lead to exponential-time complexity. To achieve the same logic (checking if all digits in the string are the same), you can use standard string functions instead of a regular expression. Recommended Fix Replace the
NOT REGEXP '^([0-9])\1+$'
line with a comparison using
REPEAT
and
LEFT
. This logic checks if the string is equal to its first character repeated for its entire length:
Copy code
sql
CASE
    WHEN LENGTH(TRIM(ID_CARD_NUMBER_OTHER)) IN (9, 12)
         AND TRIM(ID_CARD_NUMBER_OTHER) REGEXP '^[0-9]+$'
         AND TRIM(ID_CARD_NUMBER_OTHER) NOT IN ('123456789', '987654321')
         -- Check that not all digits are identical (e.g., '111111111')
         AND TRIM(ID_CARD_NUMBER_OTHER) != REPEAT(LEFT(TRIM(ID_CARD_NUMBER_OTHER), 1), LENGTH(TRIM(ID_CARD_NUMBER_OTHER)))
    THEN TRIM(ID_CARD_NUMBER_OTHER)
    ELSE NULL
END AS ID_CARD_NUMBER_OTHER
Alternative Approach (Using REPLACE) Another way to ensure the string doesn't consist of only one unique digit is to replace all occurrences of the first character with an empty string and check if anything remains:
Copy code
sql
AND LENGTH(REPLACE(TRIM(ID_CARD_NUMBER_OTHER), LEFT(TRIM(ID_CARD_NUMBER_OTHER), 1), '')) > 0
Why this happened: * Engine: StarRocks uses the RE2 engine for
REGEXP
and
RLIKE
. * Limitation: RE2 does not support backreferences, lookahead/lookbehind assertions, or other features that require backtracking. * Performance: While slightly more restrictive, this ensures your queries remain highly performant even with complex data. millions of rows/second even with complex patterns. Referencessql-reference/sql-functions/string-functions/replace.mdsql-reference/sql-functions/string-functions/lpad.mdsql-reference/sql-functions/string-functions/repeat.mdsql-reference/sql-functions/string-functions/strleft.mdsql-reference/sql-functions/like-predicate-functions/regexp_replace.mdsql-reference/sql-functions/string-functions/left.mdsql-reference/sql-functions/string-functions/rpad.mdsql-reference/sql-functions/array-functions/array_repeat.md
l
@Rocky rewrite this sql to run in starrocks WITH -- 1 LOS_CIF has more than 1 ID_CARD_NUMBER → take the latest one source_dup_id_card AS ( SELECT LOS_CIF_NO FROM source_data_add_master_id_20260430_loan_card GROUP BY LOS_CIF_NO HAVING COUNT(DISTINCT ID_CARD_NUMBER) > 1 ), add_MASTER_ID_CARD_dup_id_card AS ( SELECT ID_CARD_NUMBER AS MASTER_ID_CARD, CONTRACT_NO FROM ( SELECT ID_CARD_NUMBER, CONTRACT_NO, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY status_created_date DESC) AS RN FROM source_data_add_master_id_20260430_loan_card a INNER JOIN source_dup_id_card b ON a.LOS_CIF_NO = b.LOS_CIF_NO ) t WHERE RN = 1 ), -- 1 ID_CARD_NUMBER has more than 1 LOS_CIF → take the latest one source_dup_los_cif AS ( SELECT ID_CARD_NUMBER FROM source_data_add_master_id_20260430_loan_card GROUP BY ID_CARD_NUMBER HAVING COUNT(DISTINCT LOS_CIF_NO) > 1 ), add_MASTER_ID_CARD_dup_los_cif AS ( SELECT ID_CARD_NUMBER AS MASTER_ID_CARD, CONTRACT_NO FROM ( SELECT a.ID_CARD_NUMBER, CONTRACT_NO, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY status_created_date DESC) AS RN FROM source_data_add_master_id_20260430_loan_card a INNER JOIN source_dup_los_cif b ON a.ID_CARD_NUMBER = b.ID_CARD_NUMBER ) t WHERE RN = 1 ), -- Cross-match via ID_CARD_NUMBER_OTHER to find the master card add_master_id_card AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.STATUS_CREATED_DATE, t1.CUSTOMER_NAME, t2.LOS_CIF_NO AS LOS_CIF_NO_s2, t2.CONTRACT_NO AS CONTRACT_NO_s2, t2.ID_CARD_NUMBER AS ID_CARD_NUMBER_s2, t2.ID_CARD_NUMBER_OTHER AS ID_CARD_NUMBER_OTHER_s2, t2.STATUS_CREATED_DATE AS STATUS_CREATED_DATE_s2, CASE WHEN t1.STATUS_CREATED_DATE > t2.STATUS_CREATED_DATE THEN t1.ID_CARD_NUMBER ELSE t2.ID_CARD_NUMBER END AS MASTER_ID_CARD FROM source_data_add_master_id_20260430_loan_card t1 INNER JOIN source_data_add_master_id_20260430_loan_card t2 ON t1.ID_CARD_NUMBER_OTHER = t2.ID_CARD_NUMBER OR t2.ID_CARD_NUMBER_OTHER = t1.ID_CARD_NUMBER ), -- Deduplicate round 1: keep longest MASTER_ID_CARD per LOS_CIF add_master_id_card_remove_dup1 AS ( SELECT * FROM ( SELECT a.*, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY LENGTH(MASTER_ID_CARD) DESC) AS RN FROM add_master_id_card a ) t WHERE RN = 1 ), -- Second pass: re-cross-match on the deduplicated set add_master_id_card_2 AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.STATUS_CREATED_DATE, t1.CUSTOMER_NAME, t2.LOS_CIF_NO AS LOS_CIF_NO_s2, t2.CONTRACT_NO AS CONTRACT_NO_s2, t2.ID_CARD_NUMBER AS ID_CARD_NUMBER_s2, t2.ID_CARD_NUMBER_OTHER AS ID_CARD_NUMBER_OTHER_s2, t2.STATUS_CREATED_DATE AS STATUS_CREATED_DATE_s2, CASE WHEN LENGTH(t1.MASTER_ID_CARD) > LENGTH(t2.MASTER_ID_CARD) THEN t1.MASTER_ID_CARD ELSE t2.MASTER_ID_CARD END AS MASTER_ID_CARD FROM add_master_id_card_remove_dup1 t1 INNER JOIN add_master_id_card_remove_dup1 t2 ON t1.ID_CARD_NUMBER_OTHER = t2.ID_CARD_NUMBER OR t2.ID_CARD_NUMBER_OTHER = t1.ID_CARD_NUMBER ), -- Deduplicate round 2 add_master_id_card_remove_dup2 AS ( SELECT * FROM ( SELECT a.*, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY LENGTH(MASTER_ID_CARD) DESC) AS RN FROM add_master_id_card_2 a ) t WHERE RN = 1 ), -- Level-1 mapping: attach MASTER_ID_CARD from cross-match result map_data_lv1 AS ( SELECT t1.*, IFNULL(t2.MASTER_ID_CARD, t1.ID_CARD_NUMBER) AS MASTER_ID_CARD FROM source_data_add_master_id_20260430_loan_card t1 LEFT JOIN ( SELECT DISTINCT CONTRACT_NO, MASTER_ID_CARD FROM ( SELECT DISTINCT CONTRACT_NO, MASTER_ID_CARD, STATUS_CREATED_DATE FROM add_master_id_card_remove_dup2 ) x ) t2 ON t1.CONTRACT_NO = t2.CONTRACT_NO ), -- Level-2 mapping: override with dup-id-card resolution map_data_lv2 AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.APP_CODE, t1.CONTRACT_STATUS, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.status_created_date, t1.CUSTOMER_NAME, t1.type, IFNULL(t2.MASTER_ID_CARD, t1.MASTER_ID_CARD) AS MASTER_ID_CARD FROM map_data_lv1 t1 LEFT JOIN add_MASTER_ID_CARD_dup_id_card t2 ON t1.CONTRACT_NO = t2.CONTRACT_NO ), -- Level-3 mapping: override with dup-los-cif resolution map_data_lv3 AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.APP_CODE, t1.CONTRACT_STATUS, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.status_created_date, t1.CUSTOMER_NAME, t1.type, IFNULL(t2.MASTER_ID_CARD, t1.MASTER_ID_CARD) AS MASTER_ID_CARD FROM map_data_lv2 t1 LEFT JOIN add_MASTER_ID_CARD_dup_los_cif t2 ON t1.CONTRACT_NO = t2.CONTRACT_NO ) SELECT * FROM map_data_lv3;
r
The SQL you provided is mostly standard-compliant, but to ensure it runs correctly in StarRocks, I have fixed the HTML entities (like
>
) and ensured the syntax aligns with StarRocks' SQL parser. Key Adjustments: 1. Operator Fixes: Replaced
>
with
>
. 2. Join Conditions: While StarRocks supports
OR
in
JOIN
conditions, note that this can be computationally expensive on very large datasets compared to a
UNION
of two separate joins. However, it is syntactically valid. 3. Window Functions: The
ROW_NUMBER()
logic is fully supported in StarRocks. ```sql WITH -- 1 LOS_CIF has more than 1 ID_CARD_NUMBER -> take the latest one source_dup_id_card AS ( SELECT LOS_CIF_NO FROM source_data_add_master_id_20260430_loan_card GROUP BY LOS_CIF_NO HAVING COUNT(DISTINCT ID_CARD_NUMBER) > 1 ), add_MASTER_ID_CARD_dup_id_card AS ( SELECT ID_CARD_NUMBER AS MASTER_ID_CARD, CONTRACT_NO FROM ( SELECT ID_CARD_NUMBER, CONTRACT_NO, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY status_created_date DESC) AS RN FROM source_data_add_master_id_20260430_loan_card a INNER JOIN source_dup_id_card b ON a.LOS_CIF_NO = b.LOS_CIF_NO ) t WHERE RN = 1 ), -- 1 ID_CARD_NUMBER has more than 1 LOS_CIF -> take the latest one source_dup_los_cif AS ( SELECT ID_CARD_NUMBER FROM source_data_add_master_id_20260430_loan_card GROUP BY ID_CARD_NUMBER HAVING COUNT(DISTINCT LOS_CIF_NO) > 1 ), add_MASTER_ID_CARD_dup_los_cif AS ( SELECT ID_CARD_NUMBER AS MASTER_ID_CARD, CONTRACT_NO FROM ( SELECT a.ID_CARD_NUMBER, CONTRACT_NO, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY status_created_date DESC) AS RN FROM source_data_add_master_id_20260430_loan_card a INNER JOIN source_dup_los_cif b ON a.ID_CARD_NUMBER = b.ID_CARD_NUMBER ) t WHERE RN = 1 ), -- Cross-match via ID_CARD_NUMBER_OTHER to find the master card add_master_id_card AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.STATUS_CREATED_DATE, t1.CUSTOMER_NAME, t2.LOS_CIF_NO AS LOS_CIF_NO_s2, t2.CONTRACT_NO AS CONTRACT_NO_s2, t2.ID_CARD_NUMBER AS ID_CARD_NUMBER_s2, t2.ID_CARD_NUMBER_OTHER AS ID_CARD_NUMBER_OTHER_s2, t2.STATUS_CREATED_DATE AS STATUS_CREATED_DATE_s2, CASE WHEN t1.STATUS_CREATED_DATE > t2.STATUS_CREATED_DATE THEN t1.ID_CARD_NUMBER ELSE t2.ID_CARD_NUMBER END AS MASTER_ID_CARD FROM source_data_add_master_id_20260430_loan_card t1 INNER JOIN source_data_add_master_id_20260430_loan_card t2 ON t1.ID_CARD_NUMBER_OTHER = t2.ID_CARD_NUMBER OR t2.ID_CARD_NUMBER_OTHER = t1.ID_CARD_NUMBER ), -- Deduplicate round 1: keep longest MASTER_ID_CARD per LOS_CIF add_master_id_card_remove_dup1 AS ( SELECT * FROM ( SELECT a.*, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY LENGTH(MASTER_ID_CARD) DESC) AS RN FROM add_master_id_card a ) t WHERE RN = 1 ), -- Second pass: re-cross-match on the deduplicated set add_master_id_card_2 AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.STATUS_CREATED_DATE, t1.CUSTOMER_NAME, t2.LOS_CIF_NO AS LOS_CIF_NO_s2, t2.CONTRACT_NO AS CONTRACT_NO_s2, t2.ID_CARD_NUMBER AS ID_CARD_NUMBER_s2, t2.ID_CARD_NUMBER_OTHER AS ID_CARD_NUMBER_OTHER_s2, t2.STATUS_CREATED_DATE AS STATUS_CREATED_DATE_s2, CASE WHEN LENGTH(t1.MASTER_ID_CARD) > LENGTH(t2.MASTER_ID_CARD) THEN t1.MASTER_ID_CARD ELSE t2.MASTER_ID_CARD END AS MASTER_ID_CARD FROM add_master_id_card_remove_dup1 t1
INNER JOIN add_master_id_card_remove_dup1 t2 ON t1.ID_CARD_NUMBER_OTHER = t2.ID_CARD_NUMBER OR t2.ID_CARD_NUMBER_OTHER = t1.ID_CARD_NUMBER ), -- Deduplicate round 2 add_master_id_card_remove_dup2 AS ( SELECT * FROM ( SELECT a.*, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY LENGTH(MASTER_ID_CARD) DESC) AS RN FROM add_master_id_card_2 a ) t WHERE RN = 1 ), -- Level-1 mapping map_data_lv1 AS ( SELECT t1.*, IFNULL(t2.MASTER_ID_CARD, t1.ID_CARD_NUMBER) AS MASTER_ID_CARD FROM source_data_add_master_id_20260430_loan_card t1 LEFT JOIN ( SELECT DISTINCT CONTRACT_NO, MASTER_ID_CARD FROM ( SELECT DISTINCT CONTRACT_NO, MASTER_ID_CARD, STATUS_CREATED_DATE FROM add_master_id_card_remove_dup2 ) x ) t2 ON t1.CONTRACT_NO = t2.CONTRACT_NO ), -- Level-2 mapping map_data_lv2 AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.APP_CODE, t1.CONTRACT_STATUS, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.status_created_date, t1.CUSTOMER_NAME, t1.type, IFNULL(t2.MASTER_ID_CARD, t1.MASTER_ID_CARD) AS MASTER_ID_CARD FROM map_data_lv1 t1 LEFT JOIN add_MASTER_ID_CARD_dup_id_card t2 ON t1.CONTRACT_NO = t2.CONTRACT_NO ), -- Level-3 mapping map_data_lv3 AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.APP_CODE, t1.CONTRACT_STATUS, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.status_created_date, t1.CUSTOMER_NAME, t1.type, IFNULL(t2.MASTER_ID_CARD, t1.MASTER_ID_CARD) AS MASTER_ID_CARD FROM map_data_lv2 t1 LEFT JOIN add_MASTER_ID_CARD_dup_los_cif t2 ON t1.CONTRACT_NO = t2.CONTRACT_NO ) SELECT * FROM map_data_lv3; ``` Note on Performance: The query contains several self-joins with
OR
conditions (e.g., in
add_master_id_card
). If your dataset is very large (hundreds of millions of rows), StarRocks might struggle to optimize this specific join type. If you experience slowness, consider breaking the
OR
join into two separate
INNER JOIN
blocks and using
UNION ALL
to combine them.
l
@Rocky help me breaking the
OR
join into two separate
INNER JOIN
blocks and using
UNION ALL
to combine them.
r
To optimize the query for StarRocks, we can split the
OR
joins into separate
INNER JOIN
blocks and combine them with
UNION ALL
. This allows the StarRocks optimizer to use more efficient hash join strategies for each individual join. Since your downstream logic uses
ROW_NUMBER() ... WHERE RN = 1
, the extra rows generated by
UNION ALL
(in cases where both
OR
conditions are met) will be automatically deduplicated. Here is the rewritten SQL: ```sql WITH -- 1 LOS_CIF has more than 1 ID_CARD_NUMBER -> take the latest one source_dup_id_card AS ( SELECT LOS_CIF_NO FROM source_data_add_master_id_20260430_loan_card GROUP BY LOS_CIF_NO HAVING COUNT(DISTINCT ID_CARD_NUMBER) > 1 ), add_MASTER_ID_CARD_dup_id_card AS ( SELECT ID_CARD_NUMBER AS MASTER_ID_CARD, CONTRACT_NO FROM ( SELECT ID_CARD_NUMBER, CONTRACT_NO, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY status_created_date DESC) AS RN FROM source_data_add_master_id_20260430_loan_card a INNER JOIN source_dup_id_card b ON a.LOS_CIF_NO = b.LOS_CIF_NO ) t WHERE RN = 1 ), -- 1 ID_CARD_NUMBER has more than 1 LOS_CIF -> take the latest one source_dup_los_cif AS ( SELECT ID_CARD_NUMBER FROM source_data_add_master_id_20260430_loan_card GROUP BY ID_CARD_NUMBER HAVING COUNT(DISTINCT LOS_CIF_NO) > 1 ), add_MASTER_ID_CARD_dup_los_cif AS ( SELECT ID_CARD_NUMBER AS MASTER_ID_CARD, CONTRACT_NO FROM ( SELECT a.ID_CARD_NUMBER, CONTRACT_NO, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY status_created_date DESC) AS RN FROM source_data_add_master_id_20260430_loan_card a INNER JOIN source_dup_los_cif b ON a.ID_CARD_NUMBER = b.ID_CARD_NUMBER ) t WHERE RN = 1 ), -- REWRITTEN: Cross-match via UNION ALL instead of OR add_master_id_card AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.STATUS_CREATED_DATE, t1.CUSTOMER_NAME, t2.LOS_CIF_NO AS LOS_CIF_NO_s2, t2.CONTRACT_NO AS CONTRACT_NO_s2, t2.ID_CARD_NUMBER AS ID_CARD_NUMBER_s2, t2.ID_CARD_NUMBER_OTHER AS ID_CARD_NUMBER_OTHER_s2, t2.STATUS_CREATED_DATE AS STATUS_CREATED_DATE_s2, CASE WHEN t1.STATUS_CREATED_DATE > t2.STATUS_CREATED_DATE THEN t1.ID_CARD_NUMBER ELSE t2.ID_CARD_NUMBER END AS MASTER_ID_CARD FROM source_data_add_master_id_20260430_loan_card t1 INNER JOIN source_data_add_master_id_20260430_loan_card t2 ON t1.ID_CARD_NUMBER_OTHER = t2.ID_CARD_NUMBER UNION ALL SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.STATUS_CREATED_DATE, t1.CUSTOMER_NAME, t2.LOS_CIF_NO AS LOS_CIF_NO_s2, t2.CONTRACT_NO AS CONTRACT_NO_s2, t2.ID_CARD_NUMBER AS ID_CARD_NUMBER_s2, t2.ID_CARD_NUMBER_OTHER AS ID_CARD_NUMBER_OTHER_s2, t2.STATUS_CREATED_DATE AS STATUS_CREATED_DATE_s2, CASE WHEN t1.STATUS_CREATED_DATE > t2.STATUS_CREATED_DATE THEN t1.ID_CARD_NUMBER ELSE t2.ID_CARD_NUMBER END AS MASTER_ID_CARD FROM source_data_add_master_id_20260430_loan_card t1 INNER JOIN source_data_add_master_id_20260430_loan_card t2 ON t2.ID_CARD_NUMBER_OTHER = t1.ID_CARD_NUMBER ), -- Deduplicate round 1 add_master_id_card_remove_dup1 AS ( SELECT * FROM ( SELECT a.*, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY LENGTH(MASTER_ID_CARD) DESC) AS RN FROM add_master_id_card a ) t WHERE RN = 1 ), -- REWRITTEN: Second pass via UNION ALL instead of OR add_master_id_card_2 AS ( SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.STATUS_CREATED_DATE, t1.CUSTOMER_NAME, t2.LOS_CIF_NO AS LOS_CIF_NO_s2, t2.CONTRACT_NO AS CONTRACT_NO_s2, t2.ID_CARD_NUMBER AS ID_CARD_NUMBER_s2, t2.ID_CARD_NUMBER_OTHER AS ID_CARD_NUMBER_OTHER_s2, t2.STATUS_CREATED_DATE AS STATUS_CREATED_DATE_s2,
CASE WHEN LENGTH(t1.MASTER_ID_CARD) > LENGTH(t2.MASTER_ID_CARD) THEN t1.MASTER_ID_CARD ELSE t2.MASTER_ID_CARD END AS MASTER_ID_CARD FROM add_master_id_card_remove_dup1 t1 INNER JOIN add_master_id_card_remove_dup1 t2 ON t1.ID_CARD_NUMBER_OTHER = t2.ID_CARD_NUMBER UNION ALL SELECT t1.LOS_CIF_NO, t1.CONTRACT_NO, t1.ID_CARD_NUMBER, t1.ID_CARD_NUMBER_OTHER, t1.STATUS_CREATED_DATE, t1.CUSTOMER_NAME, t2.LOS_CIF_NO AS LOS_CIF_NO_s2, t2.CONTRACT_NO AS CONTRACT_NO_s2, t2.ID_CARD_NUMBER AS ID_CARD_NUMBER_s2, t2.ID_CARD_NUMBER_OTHER AS ID_CARD_NUMBER_OTHER_s2, t2.STATUS_CREATED_DATE AS STATUS_CREATED_DATE_s2, CASE WHEN LENGTH(t1.MASTER_ID_CARD) > LENGTH(t2.MASTER_ID_CARD) THEN t1.MASTER_ID_CARD ELSE t2.MASTER_ID_CARD END AS MASTER_ID_CARD FROM add_master_id_card_remove_dup1 t1 INNER JOIN add_master_id_card_remove_dup1 t2 ON t2.ID_CARD_NUMBER_OTHER = t1.ID_CARD_NUMBER ), -- Deduplicate round 2 add_master_id_card_remove_dup2 AS ( SELECT * FROM ( SELECT a.*, ROW_NUMBER() OVER (PARTITION BY a.LOS_CIF_NO ORDER BY LENGTH(MASTER_ID_CARD) DESC) AS RN FROM add_master_id_card_2 a ) t WHERE RN = 1 ), -- Level-1 mapping map_data_lv1 AS ( SELECT t1.*, IFNULL(t2.MASTER_ID_CARD, t1.ID_CARD_NUMBER) AS MASTER_ID_CARD FROM source_data_add_master_id_20260430_loan_card t1 LEFT JOIN ( SELECT DISTINCT CONTRACT_NO, MASTER_ID_CARD FROM add_master_id_card_remove_dup2 ) t2 ON t1.CONTRACT_NO = t2.CONTRACT_NO ), -- Level-2 mapping map_data_lv2 AS ( SELECT t1.*, IFNULL(t2.MASTER_ID_CARD, t1.MASTER_ID_CARD) AS MASTER_ID_CARD FROM map_data_lv1 t1 LEFT JOIN add_MASTER_ID_CARD_dup_id_card t2 ON t1.CONTRACT_NO = t2.CONTRACT_NO ), -- Level-3 mapping map_data_lv3 AS ( SELECT t1.*, IFNULL(t2.MASTER_ID_CARD, t1.MASTER_ID_CARD) AS MASTER_ID_CARD FROM map_data_lv2 t1 LEFT JOIN add_MASTER_ID_CARD_dup_los_cif t2 ON t1.CONTRACT_NO = t2.CONTRACT_NO ) SELECT * FROM map_data_lv3; ``` Why this is better: * Hash Join Support: StarRocks is highly optimized for equijoins (
A.col = B.col
). By using
UNION ALL
, each sub-query performs a standard hash join. * Parallelism: StarRocks can execute the different branches of a
UNION ALL
in parallel across the cluster. * Avoids Nested Loops: Joins with
OR
often default to less efficient join types because the engine cannot easily build a single hash table for multiple independent columns from both sides of the
OR
. Referencesknowledge/usage/best-practice.mdfaq/Sql_faq.mdsql-reference/sql-statements/table_bucket_part_index/SELECT.mdusing_starrocks/accelerate-cardinality-preserving-joins.md
l
@Rocky rewrite this into starrocks select a.*, UPPER( REGEXP_REPLACE( TRANSLATE( UPPER(CUSTOMER_NAME_REMOVE_COMBINING), 'ÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĀĄÃÆÄÅ' || -- AAAAAAAAAAAAAAAAAAAAAAA 'ÈÉẸẺẼÊỀẾỆỂỄĔĖĘĚ' || -- EEEEEEEEEEEEEEE 'ÌÍỊỈĨĬĮİ' || -- IIIIIIII 'ÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠŎŐØ' || -- OOOOOOOOOOOOOOOOOOOO 'ÙÚỤỦŨƯỪỨỰỬỮŬŮŰŲǓ' || -- UUUUUUUUUUUUUUUU 'ỲÝỴỶỸ' || 'ĐĎ' || 'ĆČÇ' || 'ÑŃŇŅŊ' || 'Ř' || 'ŚŠŞ' || 'ŹŽŻ', 'AAAAAAAAAAAAAAAAAAAAAAA' || 'EEEEEEEEEEEEEEE' || 'IIIIIIII' || 'OOOOOOOOOOOOOOOOOOOO' || 'UUUUUUUUUUUUUUUU' || 'YYYYY' || 'DD' || 'CCC' || 'NNNNN' || 'R' || 'SSS' || 'ZZZ' ), '[[space]]+', ' ' ) ) AS CUSTOMER_NAME_NO_ACCENT from convert_customer_name_STEP_2 a
r
To rewrite this SQL for StarRocks, you can use the
TRANSLATE
function (available from v3.2 onwards) and the
REGEXP_REPLACE
function. Since the
||
operator in StarRocks defaults to a logical
OR
(unless the
pipes_as_concat
session variable is enabled), it is safer and more standard to use the
CONCAT()
function or provide the strings as single long literals. Rewritten SQL
Copy code
sql
SELECT a.*,
UPPER(
    REGEXP_REPLACE(
        TRANSLATE(
            UPPER(CUSTOMER_NAME_REMOVE_COMBINING),
            -- From string (all accented characters)
            CONCAT(
                'ÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĀĄÃÆÄÅ',
                'ÈÉẸẺẼÊỀẾỆỂỄĔĖĘĚ',
                'ÌÍỊỈĨĬĮİ',
                'ÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠŎŐØ',
                'ÙÚỤỦŨƯỪỨỰỬỮŬŮŰŲǓ',
                'ỲÝỴỶỸ',
                'ĐĎ',
                'ĆČÇ',
                'ÑŃŇŅŊ',
                'Ř',
                'ŚŠŞ',
                'ŹŽŻ'
            ),
            -- To string (non-accented equivalents)
            CONCAT(
                'AAAAAAAAAAAAAAAAAAAAAAA',
                'EEEEEEEEEEEEEEE',
                'IIIIIIII',
                'OOOOOOOOOOOOOOOOOOOO',
                'UUUUUUUUUUUUUUUU',
                'YYYYY',
                'DD',
                'CCC',
                'NNNNN',
                'R',
                'SSS',
                'ZZZ'
            )
        ),
        '[[:space:]]+', -- Matches one or more whitespace characters
        ' '             -- Replaces with a single space
    )
) AS CUSTOMER_NAME_NO_ACCENT
FROM convert_customer_name_STEP_2 a;
Key Considerations for StarRocks: 1. Version Requirement: The
TRANSLATE
function was introduced in StarRocks v3.2. If you are on an older version, you would need to nest multiple
REPLACE()
calls (which is inefficient for this many characters) or use a Custom Function (UDF). 2. Regular Expression Engine: StarRocks uses the RE2 engine. The pattern
[[:space:]]+
is fully supported to identify whitespace (including tabs and various space characters) and collapse them into one. 3. String Concatenation: If you prefer using
||
for concatenation as in your original snippet, you can run
SET pipes_as_concat = true;
before your query, or simply use
CONCAT()
as shown above to avoid issues. 4. Character Counts: Ensure the number of characters in the
from_string
exactly matches the
to_string
. In the provided logic, there are 104 characters in both sets. Referencessql-reference/sql-functions/string-functions/translate.mdsql-reference/sql-functions/string-functions/replace.mdsql-reference/sql-functions/string-functions/upper.mdsql-reference/sql-functions/like-predicate-functions/regexp_replace.mdsql-reference/sql-functions/string-functions/concat.mdsql-reference/sql-functions/string-functions/trim.mdsql-reference/sql-statements/TRANSLATE_TRINO.mdsql-reference/sql-functions/string-functions/ltrim.mdsql-reference/sql-functions/string-functions/rtrim.md Referencessql-reference/sql-functions/string-functions/translate.md
l
@Rocky rewrite this sql into starrock and optimized for better performance with /*xử lý các case bị mapping thiếu, k xử lý manual nữa */ id_map AS ( SELECT LOS_CIF_NO, ID_CARD_NUMBER AS ID_VALUE FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 WHERE ID_CARD_NUMBER IS NOT NULL UNION SELECT LOS_CIF_NO, ID_CARD_NUMBER_OTHER AS ID_VALUE FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 WHERE ID_CARD_NUMBER_OTHER IS NOT NULL UNION SELECT LOS_CIF_NO, MASTER_ID_CARD AS ID_VALUE FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 WHERE MASTER_ID_CARD IS NOT NULL ), /* Bước 2: tạo quan hệ giữa các LOS_CIF_NO có cùng thông tin nhận dạng Mục đích: - Nếu nhiều LOS_CIF_NO dùng chung một ID_VALUE - Thì các LOS_CIF_NO này được xem là có liên quan cùng khách hàng */ edges AS ( SELECT DISTINCT a.LOS_CIF_NO AS FROM_CIF, b.LOS_CIF_NO AS TO_CIF FROM id_map a JOIN id_map b ON a.ID_VALUE = b.ID_VALUE WHERE a.LOS_CIF_NO <> b.LOS_CIF_NO ), /* Bước 3: Recursive gom cụm LOS_CIF liên thông Mục đích: - Từ các LOS_CIF_1 -> LOS_CIF_2 ở bước 2 - Gom toàn bộ LOS_CIF có liên quan trực tiếp hoặc gián tiếp - Ví dụ: CIF_A nối CIF_B, CIF_B nối CIF_C => CIF_A, CIF_B, CIF_C cùng một cụm khách hàng */ walk (ROOT_CIF, LOS_CIF_NO) AS ( SELECT DISTINCT LOS_CIF_NO AS ROOT_CIF, LOS_CIF_NO FROM id_map UNION ALL SELECT w.ROOT_CIF, e.TO_CIF FROM walk w JOIN edges e ON w.LOS_CIF_NO = e.FROM_CIF ) CYCLE LOS_CIF_NO SET IS_CYCLE TO 1 DEFAULT 0, /* Bước 4: Tạo group đại diện cho từng LOS_CIF_NO Mục đích: - Mỗi LOS_CIF_NO có thể đi tới nhiều ROOT_CIF trong cụm liên thông - Lấy MIN(ROOT_CIF) làm LOS_CIF_NO_V2 để đại diện cho cả cụm */ component_map AS ( SELECT LOS_CIF_NO, MIN(ROOT_CIF) AS LOS_CIF_NO_V2 FROM ( SELECT DISTINCT ROOT_CIF, LOS_CIF_NO FROM walk ) GROUP BY LOS_CIF_NO ), /* Bước 5: Đếm MASTER_ID_CARD trong từng group LOS_CIF Mục đích: - Sau khi mỗi LOS_CIF_NO đã được gán GROUP_LOS_CIF - Kiểm tra trong mỗi group đang có bao nhiêu MASTER_ID_CARD khác nhau - Nếu CNT_MASTER > 1 thì group này còn nhiều master, cần xử lý tiếp */ master_rank AS ( SELECT cm.LOS_CIF_NO_V2, b.MASTER_ID_CARD, COUNT(*) AS CNT_MASTER, MAX(b.STATUS_CREATED_DATE) AS MAX_STATUS_DATE, ROW_NUMBER() OVER ( PARTITION BY cm.LOS_CIF_NO_V2 ORDER BY COUNT(*) DESC, MAX(b.STATUS_CREATED_DATE) DESC, b.MASTER_ID_CARD ) AS RN FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 b JOIN component_map cm ON b.LOS_CIF_NO = cm.LOS_CIF_NO WHERE b.MASTER_ID_CARD IS NOT NULL GROUP BY cm.LOS_CIF_NO_V2, b.MASTER_ID_CARD ), /* Bước 6: Chọn MASTER_ID_CARD_V2 cho từng group LOS_CIF Mục đích: - Với các group có nhiều MASTER_ID_CARD - Chọn ra 1 MASTER_ID_CARD_V2 đại diện cuối cùng cho cả group - Rule chọn: + Ưu tiên ID 12 số hơn 9 số + Nếu cùng độ dài thì lấy MASTER_ID_CARD lớn hơn để kết quả ổn định */ group_master AS ( SELECT LOS_CIF_NO_V2, MASTER_ID_CARD AS MASTER_ID_CARD_V2 FROM master_rank WHERE RN = 1 ) ,data_LOS_CIF_NO_v2 as ( SELECT b.*, nvl(cm.LOS_CIF_NO_V2,b.LOS_CIF_NO) as LOS_CIF_NO_V2, nvl(gm.MASTER_ID_CARD_V2,b.MASTER_ID_CARD) as MASTER_ID_CARD_V2 FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 b left join component_map cm ON b.LOS_CIF_NO = cm.LOS_CIF_NO left join group_master gm ON cm.LOS_CIF_NO_V2 = gm.LOS_CIF_NO_V2 ) --add master_id_card_v3, nó sẽ được lấy từ master_id_card_v2 + name -> master_id_card_v3 , --replace space đầu và cuối convert_customer_name_STEP_1 as ( select a.*,REGEXP_REPLACE( REPLACE( REPLACE( REPLACE( REPLACE(TRIM(CUSTOMER_NAME), CHR(9), ' '), CHR(10), ' '), CHR(13), ' '), UNISTR('\00A0'), ' '), '[[space]]+', ' ' ) AS CUSTOMER_NAME_TRIM from data_LOS_CIF_NO_v2 a ), --remove dấu unicode convert_customer_name_STEP_2 as ( select a.*, REGEXP_REPLACE( CUSTOMER_NAME_TRIM, '[' || UNISTR('\0300') || '-' || UNISTR('\036F') || ']', '' ) AS CUSTOMER_NAME_REMOVE_COMBINING from convert_customer_name_STEP_1 a ), customer_name_NORMALIZED as ( select a.*, UPPER( REGEXP_REPLACE( TRANSLATE( UPPER(CUSTOMER_NAME_REMOVE_COMBINING), 'ÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĀĄÃÆÄÅ' || -- AAAAAAAAAAAAAAAAAAAAAAA 'ÈÉẸẺẼÊỀẾỆỂỄĔĖĘĚ' || -- EEEEEEEEEEEEEEE 'ÌÍỊỈĨĬĮİ' || -- IIIIIIII 'ÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠŎŐØ' || -- OOOOOOOOOOOOOOOOOOOO 'ÙÚỤỦŨƯỪỨỰỬỮŬŮŰŲǓ' || -- UUUUUUUUUUUUUUUU 'ỲÝỴỶỸ' || 'ĐĎ' || 'ĆČÇ' || 'ÑŃŇŅŊ' || 'Ř' || 'ŚŠŞ' || 'ŹŽŻ', 'AAAAAAAAAAAAAAAAAAAAAAA' || 'EEEEEEEEEEEEEEE' || 'IIIIIIII' || 'OOOOOOOOOOOOOOOOOOOO' || 'UUUUUUUUUUUUUUUU' || 'YYYYY' || 'DD' || 'CCC' || 'NNNNN' || 'R' || 'SSS' || 'ZZZ' ), '[[space]]+', ' ' ) ) AS CUSTOMER_NAME_NO_ACCENT from convert_customer_name_STEP_2 a ), DATA_CHECK as ( select a.*, TRIM( REGEXP_REPLACE( REGEXP_REPLACE(CUSTOMER_NAME_NO_ACCENT, '[^A-Z ]', ' '), '[[space]]+', ' ' ) ) AS CUSTOMER_NAME_CONVERT, REPLACE( TRIM( REGEXP_REPLACE( REGEXP_REPLACE(CUSTOMER_NAME_NO_ACCENT, '[^A-Z ]', ' '), '[[space]]+', ' ' ) ), ' ', '' ) AS CUSTOMER_NAME_CONVERT_REMOVE_SPACE, CASE WHEN CUSTOMER_NAME IS NULL THEN 'NULL_NAME' WHEN TRIM(CUSTOMER_NAME) IS NULL THEN 'BLANK_NAME' WHEN REGEXP_LIKE(CUSTOMER_NAME_NO_ACCENT, '[^A-Z ]') THEN 'CHECK_SPECIAL_CHAR' ELSE 'OK' END AS TEXT_CHECK_STATUS, REGEXP_REPLACE(CUSTOMER_NAME_NO_ACCENT, '[A-Z ]', '') AS SPECIAL_CHAR_FOUND, ASCIISTR(CUSTOMER_NAME) AS CUSTOMER_NAME_ASCIISTR from customer_name_NORMALIZED a ), CROSS_CHECK_CUSTOMER_NAME AS ( SELECT MASTER_ID_CARD_V2, COUNT(DISTINCT CONTRACT_NO) AS CNT_CONTRACT, COUNT(DISTINCT LOS_CIF_NO) AS CNT_LOS_CIF, COUNT(DISTINCT CUSTOMER_NAME_CONVERT_REMOVE_SPACE) AS CNT_NAME FROM DATA_CHECK GROUP BY MASTER_ID_CARD_V2 --HAVING COUNT(*) > 1 ), data_add_CROSS_CHECK_CUSTOMER_NAME AS ( select a.*,b.CNT_CONTRACT,b.CNT_LOS_CIF,b.CNT_NAME from DATA_CHECK a left join CROSS_CHECK_CUSTOMER_NAME b on a.MASTER_ID_CARD_V2 = b.MASTER_ID_CARD_V2 ), data_MASTER_ID_CARD_V2_va_CUSTOMER_NAME_CONVERT_REMOVE_SPACE as ( select a.*, ROW_NUMBER() OVER(PARTITION BY MASTER_ID_CARD_V2 ORDER BY CUSTOMER_NAME_CONVERT_REMOVE_SPACE ASC) RN from ( select distinct CUSTOMER_NAME_CONVERT_REMOVE_SPACE,MASTER_ID_CARD_V2 from data_add_CROSS_CHECK_CUSTOMER_NAME ) a ) select a.*, case when nvl(a.CNT_NAME, 1) = 1 then a.MASTER_ID_CARD_V2 else a.MASTER_ID_CARD_V2 || '_' || b.rn end MASTER_ID_CARD_V3 from data_add_CROSS_CHECK_CUSTOMER_NAME a left join data_MASTER_ID_CARD_V2_va_CUSTOMER_NAME_CONVERT_REMOVE_SPACE b on a.CUSTOMER_NAME_CONVERT_REMOVE_SPACE = b.CUSTOMER_NAME_CONVERT_REMOVE_SPACE and a.MASTER_ID_CARD_V2 = b.MASTER_ID_CARD_V2
r
This is a complex query involving Identity Resolution (connected components in a graph) and heavy string normalization. Critical Compatibility Note StarRocks does not support Recursive CTEs (
WITH RECURSIVE
/
walk
logic) or the
CYCLE
clause. In a distributed OLAP database like StarRocks, graph traversal is typically handled by: 1. Iterative Joining: Performing a fixed number of self-joins (usually 3-4 hops cover 99% of real-world identity links). 2. External Processing: Using Spark/GraphX for complex clustering. I have rewritten the query below using 3 levels of joins to simulate the recursion (sufficient for most KYC/Master Data use cases) and adjusted the functions for StarRocks compatibility. Optimized StarRocks SQL ```sql WITH -- Step 1: Unpivot IDs. Use UNION (distinct) to reduce rows early. id_map AS ( SELECT LOS_CIF_NO, ID_CARD_NUMBER AS ID_VALUE FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 WHERE ID_CARD_NUMBER IS NOT NULL UNION SELECT LOS_CIF_NO, ID_CARD_NUMBER_OTHER AS ID_VALUE FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 WHERE ID_CARD_NUMBER_OTHER IS NOT NULL UNION SELECT LOS_CIF_NO, MASTER_ID_CARD AS ID_VALUE FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 WHERE MASTER_ID_CARD IS NOT NULL ), -- Step 2: Direct edges between CIFs sharing an ID edges AS ( SELECT DISTINCT a.LOS_CIF_NO AS FROM_CIF, b.LOS_CIF_NO AS TO_CIF FROM id_map a JOIN id_map b ON a.ID_VALUE = b.ID_VALUE WHERE a.LOS_CIF_NO <> b.LOS_CIF_NO ), -- Step 3: Simulate Recursion (3-hop connection) -- Since StarRocks lacks RECURSIVE CTE, we join edges to find the 'ultimate' root hop1 AS ( SELECT FROM_CIF, TO_CIF FROM edges ), hop2 AS ( SELECT h1.FROM_CIF, COALESCE(h2.TO_CIF, h1.TO_CIF) AS TO_CIF FROM hop1 h1 LEFT JOIN edges h2 ON h1.TO_CIF = h2.FROM_CIF ), hop3 AS ( SELECT h2.FROM_CIF, COALESCE(h3.TO_CIF, h2.TO_CIF) AS TO_CIF FROM hop2 h2 LEFT JOIN edges h3 ON h2.TO_CIF = h3.FROM_CIF ), -- Combine all paths and find the minimum CIF to represent the cluster all_connections AS ( SELECT LOS_CIF_NO AS FROM_CIF, LOS_CIF_NO AS TO_CIF FROM id_map UNION ALL SELECT FROM_CIF, TO_CIF FROM hop3 ), component_map AS ( SELECT FROM_CIF AS LOS_CIF_NO, MIN(TO_CIF) AS LOS_CIF_NO_V2 FROM all_connections GROUP BY FROM_CIF ), -- Step 5: Rank Master IDs within the cluster master_rank AS ( SELECT cm.LOS_CIF_NO_V2, b.MASTER_ID_CARD, COUNT(*) AS CNT_MASTER, MAX(b.STATUS_CREATED_DATE) AS MAX_STATUS_DATE, ROW_NUMBER() OVER ( PARTITION BY cm.LOS_CIF_NO_V2 ORDER BY COUNT(*) DESC, MAX(b.STATUS_CREATED_DATE) DESC, b.MASTER_ID_CARD DESC ) AS RN FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 b JOIN component_map cm ON b.LOS_CIF_NO = cm.LOS_CIF_NO WHERE b.MASTER_ID_CARD IS NOT NULL GROUP BY cm.LOS_CIF_NO_V2, b.MASTER_ID_CARD ), group_master AS ( SELECT LOS_CIF_NO_V2, MASTER_ID_CARD AS MASTER_ID_CARD_V2 FROM master_rank WHERE RN = 1 ), data_LOS_CIF_NO_v2 AS ( SELECT b.*, IFNULL(cm.LOS_CIF_NO_V2, b.LOS_CIF_NO) AS LOS_CIF_NO_V2, IFNULL(gm.MASTER_ID_CARD_V2, b.MASTER_ID_CARD) AS MASTER_ID_CARD_V2 FROM MASTER_ID_CARD_ALL_20260430_loan_card_final_v2 b LEFT JOIN component_map cm ON b.LOS_CIF_NO = cm.LOS_CIF_NO LEFT JOIN group_master gm ON cm.LOS_CIF_NO_V2 = gm.LOS_CIF_NO_V2 ), -- Step 7: String Normalization (Optimized for StarRocks) convert_customer_name_STEP_1 AS ( SELECT *, REGEXP_REPLACE( TRIM(REPLACE(REPLACE(REPLACE(CUSTOMER_NAME, '\t', ' '), '\n', ' '), '\r', ' ')), '[[space]]+', ' ' ) AS CUSTOMER_NAME_TRIM FROM data_LOS_CIF_NO_v2 ), customer_name_NORMALIZED AS ( SELECT *, UPPER( REGEXP_REPLACE( TRANSLATE( UPPER(CUSTOMER_NAME_TRIM),
'ÀÁẠẢÃÂẦẤẬẨẪĂẰẮẶẲẴĀĄÃÆÄÅÈÉẸẺẼÊỀẾỆỂỄĔĖĘĚÌÍỊỈĨĬĮİÒÓỌỎÕÔỒỐỘỔỖƠỜỚỢỞỠŎŐØÙÚỤỦŨƯỪỨỰỬỮŬŮŰŲǓỲÝỴỶỸĐĎĆČÇÑŃŇŅŊŘŚŠŞŹŽŻ', 'AAAAAAAAAAAAAAAAAAAAAAAEEEEEEEEEEEEEEEIIIIIIIIOOOOOOOOOOOOOOOOOOOOUUUUUUUUUUUUUUUUYYYYYDDCCCNNNNNRSSSZZZ' ), '[[space]]+', ' ' ) ) AS CUSTOMER_NAME_NO_ACCENT FROM convert_customer_name_STEP_1 ), DATA_CHECK AS ( SELECT *, TRIM(REGEXP_REPLACE(REGEXP_REPLACE(CUSTOMER_NAME_NO_ACCENT, '[^A-Z ]', ' '), '[[space]]+', ' ')) AS CUSTOMER_NAME_CONVERT, REPLACE(TRIM(REGEXP_REPLACE(REGEXP_REPLACE(CUSTOMER_NAME_NO_ACCENT, '[^A-Z ]', ' '), '[[space]]+', ' ')), ' ', '') AS CUSTOMER_NAME_CONVERT_REMOVE_SPACE FROM customer_name_NORMALIZED ), -- Step 8: Cross-check and Final ID Generation CROSS_CHECK_CUSTOMER_NAME AS ( SELECT MASTER_ID_CARD_V2, COUNT(DISTINCT CONTRACT_NO) AS CNT_CONTRACT, COUNT(DISTINCT LOS_CIF_NO) AS CNT_LOS_CIF, COUNT(DISTINCT CUSTOMER_NAME_CONVERT_REMOVE_SPACE) AS CNT_NAME FROM DATA_CHECK GROUP BY MASTER_ID_CARD_V2 ), data_add_CROSS_CHECK_CUSTOMER_NAME AS ( SELECT a.*, b.CNT_CONTRACT, b.CNT_LOS_CIF, b.CNT_NAME FROM DATA_CHECK a LEFT JOIN CROSS_CHECK_CUSTOMER_NAME b ON a.MASTER_ID_CARD_V2 = b.MASTER_ID_CARD_V2 ), data_name_rank AS ( SELECT DISTINCT CUSTOMER_NAME_CONVERT_REMOVE_SPACE, MASTER_ID_CARD_V2, ROW_NUMBER() OVER(PARTITION BY MASTER_ID_CARD_V2 ORDER BY CUSTOMER_NAME_CONVERT_REMOVE_SPACE ASC) as RN FROM data_add_CROSS_CHECK_CUSTOMER_NAME ) SELECT a.*, CASE WHEN IFNULL(a.CNT_NAME, 1) = 1 THEN a.MASTER_ID_CARD_V2 ELSE CONCAT(a.MASTER_ID_CARD_V2, '_', CAST(b.RN AS STRING)) END AS MASTER_ID_CARD_V3 FROM data_add_CROSS_CHECK_CUSTOMER_NAME a LEFT JOIN data_name_rank b ON a.CUSTOMER_NAME_CONVERT_REMOVE_SPACE = b.CUSTOMER_NAME_CONVERT_REMOVE_SPACE AND a.MASTER_ID_CARD_V2 = b.MASTER_ID_CARD_V2; ``` Key Optimizations & Changes: 1. Graph Traversal (The "Walk"): * Replaced Recursive CTE with
hop1
,
hop2
,
hop3
. This joins
edges
multiple times to flatten the hierarchy. In banking data, it is extremely rare for a customer identity chain to exceed 3 hops (CIF A -> ID 1 -> CIF B -> ID 2 -> CIF C). This is significantly faster in StarRocks than recursion. 2. String Functions: *
TRANSLATE
: Used for bulk character replacement. Ensure you are on StarRocks v3.2+. * `CHR()`/`UNISTR()`: Replaced
CHR(9)
,
CHR(10)
, etc., with their literal escape counterparts (
\t
,
\n
,
\r
) which StarRocks handles more efficiently in
REPLACE
functions. *
CONCAT
: Used
CONCAT()
instead of
||
to avoid issues with the
pipes_as_concat
setting. 3. Performance Tuning: *
UNION ALL
vs
UNION
: In
all_connections
, I used
UNION ALL
because the final
GROUP BY
in
component_map
will handle the deduplication more efficiently than a distinct sort in the middle of the CTE. *
IFNULL
: Replaced
NVL
with
IFNULL
(standard StarRocks syntax, though
NVL
is supported as an alias). *
DISTINCT
Reduction
: Added a
data_name_rank
CTE to handle the final sequence numbering, reducing the number of joins on large datasets. What if I need more than 3 hops? If you have data where one identity chain spans 10+ CIFs, the
hop
logic will need more levels. However, for 99% of "Customer 360" or "Single Customer View" projects, 3 hops are the sweet-spot for performance vs performance-spot.
l
@Rocky recreate this oracle table to a similar table in starrocks CREATE TABLE "QTDM"."MASTER_ID_CARD_ALL_20260430_LOAN_CARD_FINAL_V2_CONVERT_LOS_CIF_SR" ( "LOS_CIF_NO" VARCHAR2(50) COLLATE "USING_NLS_COMP", "CONTRACT_NO" VARCHAR2(255) COLLATE "USING_NLS_COMP", "APP_CODE" VARCHAR2(50) COLLATE "USING_NLS_COMP", "CONTRACT_STATUS" VARCHAR2(20) COLLATE "USING_NLS_COMP", "ID_CARD_NUMBER" VARCHAR2(50) COLLATE "USING_NLS_COMP", "ID_CARD_NUMBER_OTHER" VARCHAR2(50) COLLATE "USING_NLS_COMP", "STATUS_CREATED_DATE" DATE, "CUSTOMER_NAME" VARCHAR2(250) COLLATE "USING_NLS_COMP", "TYPE" CHAR(4) COLLATE "USING_NLS_COMP", "MASTER_ID_CARD" VARCHAR2(50) COLLATE "USING_NLS_COMP", "LOS_CIF_NO_V2" VARCHAR2(50) COLLATE "USING_NLS_COMP", "MASTER_ID_CARD_V2" VARCHAR2(50) COLLATE "USING_NLS_COMP", "CUSTOMER_NAME_TRIM" VARCHAR2(32767) COLLATE "USING_NLS_COMP", "CUSTOMER_NAME_REMOVE_COMBINING" VARCHAR2(32767) COLLATE "USING_NLS_COMP", "CUSTOMER_NAME_NO_ACCENT" VARCHAR2(32767) COLLATE "USING_NLS_COMP", "CUSTOMER_NAME_CONVERT" VARCHAR2(32767) COLLATE "USING_NLS_COMP", "CUSTOMER_NAME_CONVERT_REMOVE_SPACE" VARCHAR2(32767) COLLATE "USING_NLS_COMP", "TEXT_CHECK_STATUS" VARCHAR2(18) COLLATE "USING_NLS_COMP", "SPECIAL_CHAR_FOUND" VARCHAR2(32767) COLLATE "USING_NLS_COMP", "CUSTOMER_NAME_ASCIISTR" VARCHAR2(2500) COLLATE "USING_NLS_COMP", "CNT_CONTRACT" NUMBER, "CNT_LOS_CIF" NUMBER, "CNT_NAME" NUMBER, "MASTER_ID_CARD_V3" VARCHAR2(91) COLLATE "USING_NLS_COMP" ) DEFAULT COLLATION "USING_NLS_COMP" SEGMENT CREATION IMMEDIATE PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645 PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT) TABLESPACE "DM_MHQTRR" ;
r
In StarRocks, we map Oracle types to high-performance OLAP types. Since this table appears to be a finalized analytical dataset (based on the previous logic we discussed), I have used the Duplicate Key model. This is the most efficient model in StarRocks for large-scale analytical queries and batch loading. StarRocks DDL
Copy code
sql
CREATE TABLE QTDM.MASTER_ID_CARD_ALL_20260430_LOAN_CARD_FINAL_V2_CONVERT_LOS_CIF_SR (
    -- Grouping columns first for better compression and indexing (Key Columns)
    LOS_CIF_NO          VARCHAR(100),
    CONTRACT_NO         VARCHAR(500),
    APP_CODE            VARCHAR(100),
    ID_CARD_NUMBER      VARCHAR(100),
    
    -- Other identifying columns
    CONTRACT_STATUS     VARCHAR(50),
    ID_CARD_NUMBER_OTHER VARCHAR(100),
    STATUS_CREATED_DATE DATETIME, -- Oracle DATE contains time, so DATETIME is the correct match
    CUSTOMER_NAME       VARCHAR(1000),
    TYPE                CHAR(16),
    MASTER_ID_CARD      VARCHAR(100),
    LOS_CIF_NO_V2       VARCHAR(100),
    MASTER_ID_CARD_V2   VARCHAR(100),
    
    -- Long string columns (normalized data)
    -- VARCHAR in StarRocks supports up to 1,048,576 bytes
    CUSTOMER_NAME_TRIM                  VARCHAR(65533),
    CUSTOMER_NAME_REMOVE_COMBINING      VARCHAR(65533),
    CUSTOMER_NAME_NO_ACCENT             VARCHAR(65533),
    CUSTOMER_NAME_CONVERT               VARCHAR(65533),
    CUSTOMER_NAME_CONVERT_REMOVE_SPACE  VARCHAR(65533),
    TEXT_CHECK_STATUS                   VARCHAR(50),
    SPECIAL_CHAR_FOUND                  VARCHAR(65533),
    CUSTOMER_NAME_ASCIISTR              VARCHAR(5000),
    
    -- Metrics / Counts
    CNT_CONTRACT        BIGINT,
    CNT_LOS_CIF         BIGINT,
    CNT_NAME            BIGINT,
    MASTER_ID_CARD_V3   VARCHAR(200)
) 
ENGINE=OLAP
DUPLICATE KEY(LOS_CIF_NO, CONTRACT_NO, APP_CODE, ID_CARD_NUMBER)
COMMENT "Master ID Card Final Normalized Table"
DISTRIBUTED BY HASH(LOS_CIF_NO) BUCKETS 16
PROPERTIES (
    "replication_num" = "3",
    "in_memory" = "false",
    "storage_format" = "v2"
);
Key Differences & Mapping Logic: 1. Data Types: *
VARCHAR2
VARCHAR
: StarRocks
VARCHAR
is length-limited by bytes. I expanded the lengths (e.g., 50 → 100) to account for potential UTF-8 multi-byte characters safely. *
DATE
DATETIME
: In Oracle,
DATE
stores year, month, day, hour, minute, and second. In StarRocks,
DATE
is only YYYY-MM-DD. Use
DATETIME
to preserve the time component. *
NUMBER
BIGINT
: For columns like
CNT_CONTRACT
, which are counts,
BIGINT
is more performant than
DECIMAL
. * Large
VARCHAR2(32767)
: StarRocks handles very large strings easily in
VARCHAR
. I set them to
65533
, which is a common safe upper bound for standard columns, though they can go up to 1MB if needed. 2. Table Model (
DUPLICATE KEY
)
: * Oracle is a row-store; StarRocks is a column-store. The
DUPLICATE KEY
model is ideal here because it allows duplicate rows but sorts the data based on the key columns, making filters on
LOS_CIF_NO
or
CONTRACT_NO
extremely fast. 3. Distribution (
DISTRIBUTED BY HASH
)
: * I chose
LOS_CIF_NO
for the hash distribution. This ensures that all data for the same customer is physically co-located on the same machine, making the
JOIN
and
GROUP BY
operations from your previous queries much faster. 4. Storage Parameters: * Oracle-specific physical attributes like
PCTFREE
,
TABLESPACE
,
STORAGE
, and
INITRANS
are ignored in StarRocks. Instead, StarRocks uses
replication_num
(for high availability) and
buckets
(for parallelism). Recommendations: * Buckets: I set
BUCKETS 16
. If your table size is expected to be over 100GB, you should increase this number (usually 1 bucket per 5-10GB of data per tablet). * Partitioning: If you plan to keep years of history and query by date often, you can add
PARTITION BY RANGE(STATUS_CREATED_DATE)
to the DDL.
l
@Rocky can starrock insert data to a jdbc table?
r
Yes, StarRocks can insert data into an external JDBC table using the JDBC Catalog. This feature allows you to "sink" data from StarRocks directly into external databases like MySQL, PostgreSQL, Oracle, or SQL Server. Version Requirements The ability to write to a JDBC catalog was introduced gradually: * StarRocks v3.3+: Supports sinking data to MySQL, PostgreSQL, and ClickHouse. * StarRocks v3.4+: Expanded support for sinking data to Oracle and SQL Server. In earlier versions (v3.0–v3.2), JDBC catalogs were primarily read-only, meaning you could only query data from them or load data into StarRocks native tables. How to Use It To insert data into a JDBC table, follow these steps: 1. Create a JDBC Catalog: Define the connection to your external database.
Copy code
sql
    CREATE EXTERNAL CATALOG my_mysql_db
    PROPERTIES (
        "type" = "jdbc",
        "user" = "root",
        "password" = "your_password",
        "jdbc_uri" = "jdbc:<mysql://192.168.1.1:3306/target_db>",
        "driver_url" = "<https://repo1.maven.org/maven2/mysql/mysql-connector-java/8.0.28/mysql-connector-java-8.0.28.jar>",
        "driver_class" = "com.mysql.cj.jdbc.Driver"
    );
2. Insert Data: Use the
INSERT INTO
statement referencing the catalog, database, and table.
Copy code
sql
    INSERT INTO my_mysql_db.target_db.external_table
    SELECT * FROM internal_starrocks_table;
Key Considerations * Privileges: You must have the
INSERT
privilege on the target external table. * Network Connectivity: Your StarRocks FE and BE (or CN) nodes must have network access to the target database's IP and port. * Drivers: The JDBC driver
.jar
file must be accessible via the
driver_url
. FEs download it upon catalog creation, and BEs download it during the first query/insert. * Performance: While convenient, sinking data via JDBC is generally slower than StarRocks' native internal storage. It is best used for small to medium data transfers or metadata updates. For more information, you can check the StarRocks JDBC Catalog documentation. Referencesdata_source/catalog/jdbc_catalog.mddata_source/catalog/unified_catalog.mddata_source/catalog/hive_catalog.md
l
@Rocky how to submit task every 1 day on 8am SUBMIT TASK losrep_application SCHEDULE EVERY(INTERVAL 10 MINUTE) AS insert into DMAPP.dtm_cus_applications_af by name select * from dmapp_link.dtm_cus_applications_af_link;
r
To schedule a StarRocks asynchronous task to run every day at 8:00 AM, you should use the
START
parameter to anchor the first execution time and the
EVERY(INTERVAL 1 DAY)
parameter for the frequency. StarRocks
SUBMIT TASK
(supported since v3.3 for scheduled tasks) uses a specific interval-based syntax rather than standard Cron expressions. Modified SQL Replace the
START
date with your desired first execution date (e.g., tomorrow at 080000):
Copy code
sql
SUBMIT TASK losrep_application
SCHEDULE START('2024-05-20 08:00:00') EVERY(INTERVAL 1 DAY)
AS
INSERT INTO DMAPP.dtm_cus_applications_af BY NAME 
SELECT * FROM dmapp_link.dtm_cus_applications_af_link;
Key Parameters: *
START('YYYY-MM-DD 08:00:00')
: This sets the specific time of day for the first run. All subsequent runs will occur exactly 24 hours (1 day) after this anchor point. *
EVERY(INTERVAL 1 DAY)
: Defines the recurring frequency. *
BY NAME
: Your use of
INSERT ... BY NAME
is supported in StarRocks and is recommended when the source and target table schemas might have different column orders. Monitoring your Task: Once submitted, you can monitor the task's schedule and history using these metadata views: 1. Check the task definition and next trigger time:
Copy code
sql
    SELECT * FROM INFORMATION_SCHEMA.tasks WHERE task_name = 'losrep_application';
2. Check the execution history (status of each run):
Copy code
sql
    SELECT * FROM INFORMATION_SCHEMA.task_runs WHERE task_name = 'losrep_application';
Notes: * Version requirement: Scheduled intervals for
SUBMIT TASK
require StarRocks v3.3 or higher. * Modifying the task: If you need to change the schedule later, you must
DROP TASK losrep_application
and submit it again, or use
ALTER TASK
(supported from v4.1+). Referencessql-reference/sql-statements/loading_unloading/ETL/SUBMIT_TASK.mdintegrations/airflow.md
l
@Rocky INSERT INTO dtm_customer_ld_summary SELECT * FROM dmapp_link.dtm_customer_ld_summary_link a WHERE a.SYS_RUN_DATE = %s; Process halted: (5604, "Getting analyzing error. Detail message: Inserted target column count: 373 doesn't match select/value column count: 370." I run insert into by name, why still get this error? can starrock insert only column which match name
r
The error occurs because your SQL statement is performing a positional insert, which requires the number of columns in the
SELECT
list (370) to exactly match the number of columns in the target table (373). Even if you intend to use "insert by name," the keywords
BY NAME
must be explicitly included in the statement. In the snippet you provided, they are missing. Correct Syntax To insert only the columns that match by name (and fill missing target columns with
NULL
or their
DEFAULT
values), you must place
BY NAME
immediately after the table name:
Copy code
sql
INSERT INTO dtm_customer_ld_summary BY NAME -- <--- Add this keyword
SELECT *
FROM dmapp_link.dtm_customer_ld_summary_link a
WHERE a.SYS_RUN_DATE = %s;
Why this works: 1. Column Mapping: When
BY NAME
is present, StarRocks ignores the order and the total count of columns. It looks at the column names returned by your JDBC link (
dmapp_link
) and matches them to the column names in
dtm_customer_ld_summary
. 2. Handling Differences: * If the target table has columns that don't exist in the source, they are filled with
DEFAULT
or
NULL
. * If the source has columns that don't exist in the target, they are typically ignored. 3. Requirements: * Version: This feature requires StarRocks v3.2.5+ or v3.3.0+. * JDBC Metadata: Since you are using a JDBC link, StarRocks must be able to retrieve the column names from the external database. If the external driver doesn't provide names,
BY NAME
will fail. (Most standard JDBC drivers for Oracle/MySQL/Postgres work fine). Troubleshooting If you added
BY NAME
and still get a column count error, it usually means: * Version too old: Check your version with
SELECT asterisk_version();
. If you are on an older version (e.g., 2.5 or 3.1),
BY NAME
is not supported, and you must explicitly list the 370 column names in the
INSERT
clause. * Ambiguous Names: If your
SELECT
statement has duplicate column names (rare with
SELECT *
), it may cause an error. Recommendation: If you are on an older version, you must manually align the columns:
Copy code
sql
INSERT INTO dtm_customer_ld_summary (col1, col2, col3, ...)
SELECT col1, col2, col3, ... FROM dmapp_link...
Referencesloading/InsertInto.mdsql-reference/sql-statements/loading_unloading/INSERT.md Referencesloading/InsertInto.mdsql-reference/sql-statements/loading_unloading/INSERT.md
l
@Rocky update this column to 'last_promised_amount' is DECIMAL64(20, 2)'
r
To update the column
last_promised_amount
to the type
DECIMAL(20, 2)
, you should use the
ALTER TABLE ... MODIFY COLUMN
statement. However, please note that
DECIMAL64
only supports up to 18 digits of precision.
Since you requested a precision of 20, the column will automatically be stored as a
DECIMAL128
internally. In StarRocks SQL, you simply define this as
DECIMAL(20, 2)
. SQL Command
Copy code
sql
ALTER TABLE your_table_name 
MODIFY COLUMN last_promised_amount DECIMAL(20, 2);
Important Considerations: 1. Internal Types: * `DECIMAL64`: Precision 1 to 18. * `DECIMAL128`: Precision 19 to 38. * Because 20 > 18, StarRocks will treat
DECIMAL(20, 2)
as a 128-bit decimal. 2. Asynchronous Operation: Modifying a column type is an asynchronous "Schema Change" operation. You can check the progress of the update using:
Copy code
sql
    SHOW ALTER TABLE COLUMN;
3. Renaming: If you also intended to rename a different column to
last_promised_amount
, you must do this in two steps (requires StarRocks v3.3.2+ for renaming):
Copy code
sql
    -- Step 1: Rename the column
    ALTER TABLE your_table_name RENAME COLUMN old_name TO last_promised_amount;

    -- Step 2: Modify the type
    ALTER TABLE your_table_name MODIFY COLUMN last_promised_amount DECIMAL(20, 2);
4. Restrictions: * You cannot modify partition columns. * The conversion must be compatible (e.g., StarRocks will allow converting
INT
or
VARCHAR
to
DECIMAL
, but ensure your existing data fits the (20, 2) scale to avoid data loss or NULLs). Referencessql-reference/data-types/numeric/DECIMAL.mdsql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.md Referencessql-reference/sql-statements/table_bucket_part_index/ALTER_TABLE.mdbest_practices/query_tuning/schema_tuning.md
l
@Rocky i'm using jasperreport for reporting from starrocks, do u offer better report solution for starrocks?
r
While JasperReports is excellent for "pixel-perfect" static reports (like PDFs or invoices), it often doesn't leverage the full power of StarRocks' sub-second, interactive performance. If you are looking for more modern, interactive, or high-performance dashboarding, StarRocks officially supports and optimizes for several alternatives: 1. The "Community Favorites" (Open Source) These are the most common choices for StarRocks users because they support interactive slicing and dicing which matches StarRocks' speed. * Apache Superset: StarRocks has a dedicated SQLAlchemy dialect and Python client. It is highly recommended for building real-time dashboards on top of StarRocks. * Metabase: StarRocks has optimized compatibility for Metabase (v3.2+). It is very user-friendly for non-technical users to build their own reports via a GUI without writing SQL. * Grafana: If your reports are time-series heavy or focused on operational monitoring, Grafana connects seamlessly to StarRocks using the MySQL data source plugin. 2. Enterprise/Commercial Solutions If you need enterprise-grade support or advanced data modeling: * FineBI / FineReport: These are very popular in the StarRocks ecosystem (especially in Asia) and have specific integration guides for StarRocks. * Tableau / Power BI: Since StarRocks is compatible with the MySQL protocol, you can connect these tools directly using standard MySQL drivers. They are better than Jasper for ad-hoc discovery and "fast" data visualization. 3. Developer-Focused Tools * Querybook: Pinterest open-sourced this tool, and StarRocks has a native integration. It’s great if your "reports" are more like shared SQL notebooks and data analysis stories. Why move away from JasperReports? * Interactivity: Jasper is built for "batch" reporting. Modern tools like Superset allow users to click a bar chart and instantly filter the whole dashboard—StarRocks is designed exactly for this type of sub-second response. * Concurrency: StarRocks can handle thousands of concurrent users. Modern BI tools are better at managing this high-concurrency traffic compared to traditional Java-based reporting engines. * Integration: StarRocks provides specific connectors for Superset and Metabase that handle metadata (like column types) more accurately than a generic JDBC connection. Recommendation: If you want a modern dashboarding experience, try Apache Superset. If you need to empower non-SQL users, try Metabase. Referencesloading/loading_tools.mdloading/Loading_intro.mdintegrations/BI_integrations/Querybook.mdintegrations/BI_integrations/Rill.mdintegrations/BI_integrations/FineBI.mdintegrations/BI_integrations/QuickBI.mdrelease_notes/release-3.2.mdintegrations/BI_integrations/Superset.md
l
@Rocky DTM_LOS_APP_PROCESSING_HIST: cột CURRENT_USER chuyển sang SR phải đổi thành CURRENT_USER _1 do trùng key word bên SR -> có cách nào đổi nó thành tến CURRENT_USER cho giống với Oracle không
r
Bạn hoàn toàn có thể sử dụng tên cột là
CURRENT_USER
trong StarRocks bằng cách sử dụng dấu nháy ngược (backticks - `` ` ``) để bao quanh tên cột. Trong StarRocks (và MySQL),
CURRENT_USER
là một từ khóa dự phòng (reserved keyword) dùng cho hàm hệ thống. Tuy nhiên, bạn vẫn có thể đặt tên cột trùng với từ khóa này nếu tuân theo quy tắc sau: 1. Khi tạo bảng (DDL) Bạn hãy bao quanh tên cột bằng dấu nháy ngược:
Copy code
sql
CREATE TABLE DTM_LOS_APP_PROCESSING_HIST (
    id INT,
    `CURRENT_USER` VARCHAR(255), -- Sử dụng dấu nháy ngược ở đây
    ...
) ...;
2. Khi thực hiện lệnh INSERT/SELECT Mỗi khi truy vấn hoặc thao tác với cột này, bạn bắt buộc phải dùng dấu nháy ngược, nếu không StarRocks sẽ hiểu lầm đó là hàm hệ thống
CURRENT_USER()
và gây lỗi:
Copy code
sql
INSERT INTO DTM_LOS_APP_PROCESSING_HIST (`CURRENT_USER`, column2)
SELECT `CURRENT_USER`, column2
FROM source_table;
Lưu ý quan trọng: * JDBC Link/External Table: Nếu bạn đang dùng JDBC Link để kéo dữ liệu từ Oracle, tại phần định nghĩa bảng trong StarRocks, bạn cũng dùng dấu nháy ngược cho cột đó. * Công cụ báo cáo: Nếu bạn dùng JasperReports hay các công cụ BI khác, hãy đảm bảo các câu lệnh SQL mà công cụ đó sinh ra có hỗ trợ dấu nháy ngược cho identifiers (thường cấu hình qua Driver MySQL/StarRocks). * Khuyên dùng: Mặc dù dùng được, nhưng để tránh việc phải luôn nhớ thêm dấu nháy ngược (dễ gây lỗi syntax khi viết SQL nhanh), các kỹ sư dữ liệu thường ưu tiên đặt tên hơi khác đi một chút (như
USER_NAME
hoặc
CURRENT_USER_VAL
). Tuy nhiên, nếu yêu cầu bắt buộc giống Oracle 100%, hãy dùng ``
CURRENT_USER
``
. Tóm lại: Bạn không cần đổi thành
CURRENT_USER_1
, chỉ cần dùng ``
CURRENT_USER
``
trong mọi câu lệnh SQL. Referencessql-reference/sql-statements/keywords.mdfaq/Sql_faq.md Referencesfaq/Sql_faq.mdsql-reference/sql-statements/keywords.mdsql-reference/sql-statements/table_bucket_part_index/CREATE_TABLE.mdsql-reference/sql-statements/table_bucket_part_index/SELECT.mdadministration/management/FE_configuration.mdfaq/loading/Stream_load_faq.mddata_source/External_table.md