简体   繁体   中英

SQL Update Statement based on Procedure in SAP HANA

I'm creating an update statement that generate SHA256 for table columns based on table's name

1st Step: I created a procedure that get the table columns, concatenate it all in one columns, then format to a desired format.

-- Procedure code : Extract table's columns list, conctenate it and format it

Create procedure SHA_PREP (in inp1 nvarchar(20))
as 
begin

SELECT concat(concat('hash_sha256(',STRING_AGG(A, ', ')),')')  AS Names
FROM (
    SELECT  concat('to_varbinary(IFNULL("',concat(COLUMN_NAME,'",''0''))')) as A
    FROM    SYS.TABLE_COLUMNS
    WHERE SCHEMA_NAME = 'SCHEMA_NAME' AND TABLE_NAME = :inp1
    AND COLUMN_NAME not in ('SHA')
    ORDER BY POSITION 
    );
end;

/* Result of this procedures : 
hash_sha256(
to_varbinary("ID"),to_varbinary(IFNULL("COL1",'0')),to_varbinary(IFNULL("COL2",'0')) )
*/

-- Update Statement needed 

UPDATE "SCHEMA_NAME"."TABLE_NAME"
SET "SHA" = CALL "SCHEMA_NAME"."SHA_PREP"('SCHEMA_NAME')
WHERE "ID" = 99 -- a random filter

I find a solution that suits my need, but maybe there's other easier or more suitable approchaes :

I added the update statement to my procedure, and inserted all the generated query into a temporary table column, the excuted it using EXECUTE IMMEDIATE

Create procedure SHA_PREP (in inp1 nvarchar(20))
as 
begin
/* ********************************************************** */
DECLARE SQL_STR VARCHAR(5000);

-- Create a temporary table to store a query in
create local temporary table #temp1 (QUERY varchar(5000));
-- Insert the desirable query into the QUERY column (Temp Table)
insert into #temp1(QUERY)
SELECT concat('UPDATE "SCHEMA_NAME"."TABLE_NAME" SET "SHA" =' ,concat(concat('hash_sha256(',STRING_AGG(A, ', ')),')'))
FROM (
    SELECT  concat('to_varbinary(IFNULL("',concat(COLUMN_NAME,'",''0''))')) as A
    FROM    SYS.TABLE_COLUMNS
    WHERE SCHEMA_NAME = 'SCHEMA_NAME' AND TABLE_NAME = :inp1
    AND COLUMN_NAME not in ('SHA')
    ORDER BY POSITION 
    );
end;
/* QUERY : UPDATE "SCHEMA_NAME"."TABLE_NAME" SET "SHA" = 
hash_sha256(to_varbinary("ID"),to_varbinary(IFNULL("COL1",'0')),to_varbinary(IFNULL("COL2",'0'))) */
SELECT QUERY into SQL_STR FROM "SCHEMA_NAME".#temp1;

--Excuting the query 
EXECUTE IMMEDIATE (:SQL_STR);

-- Dropping the temporary table 
DROP TABLE "SCHEMA_NAME".#temp1;

/* ********************************************************** */
end;

Any other solution or improvement are well welcomed Thank you

The solution by @SonOfHarpy technically works but has several issues, namely:

  • unnecessary use of temporary tables
  • overly complicated string assignment approach
  • use of fixed system table schema ( SYS.TABLE_COLUMNS ) instead of PUBLIC synonym
  • wrong data type and variable name for the input parameter

An improved version of the code looks like this:

create procedure SHA_PREP (in TABLE_NAME nvarchar(256))
as 
begin
declare SQL_STR nvarchar(5000);

    SELECT 
          'UPDATE "SCHEMA_NAME"."TABLE_NAME" SET "SHA"= hash_sha256(' || STRING_AGG(A, ', ') || ')'
          into SQL_STR
    FROM (
        SELECT  
            'TO_VARBINARY(IFNULL("'|| "COLUMN_NAME" ||'",''0''))' as A
        FROM TABLE_COLUMNS
        WHERE 
                "SCHEMA_NAME" = 'SCHEMA_NAME' 
           AND "TABLE_NAME" = :TABLE_NAME
           AND "COLUMN_NAME" != 'SHA'
        ORDER BY POSITION 
        );

  --  select :sql_str from dummy; -- this is for debugging output only
    EXECUTE IMMEDIATE (:SQL_STR);
end; 

By changing the CONCAT functions to the shorter || (double-pipe) operator, the code becomes a lot easier to read as the formerly nested function calls are now simple chained concatenations.

By using SELECT ... INTO variable the whole nonsense with the temporary table can be avoided, again, making the code easier to understand and less prone to problems.

The input parameter name now correctly reflects its meaning and mirrors the HANA dictionary data type for TABLE_NAME ( NVARCHAR(256) ).

The procedure now consists of two commands ( SELECT and EXECUTE IMMEDIATE ) that each performs an essential task of the procedure:

  1. Building a valid SQL update command string.
  2. Executing the SQL command.

I removed the useless line-comments but left a debugging statement as a comment in the code, so that the SQL string can be reviewed without having to execute the command. For that to work, obviously, the EXECUTE... line needs to be commented out and the debugging line has to be uncommented.

What's more worrying than the construction of the solution is its purpose. It looks as if the SHA column should be used as a kind of shorthand row-data fingerprint. The UPDATE approach certainly handles this as an after-thought activity but leaves the "finger-printing" for the time when the update gets executed.

Also, it takes an essential part of the table design (that the SHA column should contain the fingerprint) away from the table definition.

An alternative to this could be a GENERATED COLUMN:

create  table test (aaa int, bbb int);
alter table test add (sha varbinary (256) generated always as 
                        hash_sha256(to_varbinary(IFNULL("AAA",'0'))
                                 ,  to_varbinary(IFNULL("BBB",'0'))
                                    )
                       );

insert into test (aaa, bbb) values (12, 32);

select * from test;

/*
AAA BBB SHA                                                             
12  32  B6602F58690CA41488E97CD28153671356747C951C55541B6C8D8B8493EB7143
*/ 

With this, the "generator" approach could be used for table definition/modification time, but all the actual data handling would be automatically done by HANA, whenever values get changed in the table.
Also, no separate calls to the procedure will ever be necessary as the fingerprints will always be current.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM