I'm testing Dynamic Data Masking and I discovered that SQL Server propose only 4 functions out of the box:
Function
Examples
Default
Example of alter syntax: ALTER COLUMN Gender ADD MASKED WITH (FUNCTION = 'default()')
Email
Example of alter syntax: ALTER COLUMN Email ADD MASKED WITH (FUNCTION = 'email()')
Random
Example of alter syntax: ALTER COLUMN [Month] ADD MASKED WITH (FUNCTION = 'random(1, 12)')
Custom String
Example of alter syntax: ALTER COLUMN [Phone Number] ADD MASKED WITH (FUNCTION = 'partial(1,"XXXXXXX",0)')
I would like to use the last option, Custom String, because I need to:
Keep 3 prefix number
Shuffle 3 numbers in the middle
Keep 3 last numbers
So this phone number 123-456-789 will become 123-039-789
My first approach is to nest two functions together this way:
ALTER TABLE [Person].[PersonPhone]
ALTER COLUMN [PhoneNumber] NVARCHAR(25) MASKED
WITH (FUNCTION = 'partial(4,"' + (FUNCTION = 'random(100,999)') + '",4)');
How Can I execute a function inside a function?
Or is there any other option to create a custom MASK?
There is no option to combine two data masking functions in SQL Server (2016 or 2019).
I think you want to shuffle (instead of mask) the middle portion because you want the phone numbers to look like they have not been masked, but I don't think this is a good idea. It's better to mask them with X-s, so it is obvious what has been masked. If you have a validation rule somewhere else that says there can be no letters in the phone number, you can mask the middle-portion with 0-s, by using MASKED WITH (FUNCTION = 'partial(4,"000",4)').
The alternative would be not to use Dynamic Data Masking, but a custom view (or a computed column), where you can call any T-SQL function, for example:
LEFT(PhoneNumber,4)+REPLACE(STR(ABS(CHECKSUM(NEWID()))%1000,3),' ','0')+RIGHT(PhoneNumber,4)
Related
I would like to create a column on an in-memory table that generates a colour HEX code based on a person's name (another column). A quick google didn't give much so wondered if any pointers can be given here.
e.g
update colour: <some code and use username col as input> from table
In kdb+ you can run a function on a column via an update statement but there are slight differences depending on whether the function is vectorised or not. If vectorised:
update colour:{<some code>}[username] from table
update colour:someFunction[username] from table
If not vectorised then an iterator like each ' is required
update colour:{<some code>}'[username] from table
update colour:someFunction'[username] from table
This function will generate hex codes from the first 3 characters of a string.
q)hex:{a:i-16*j:(i:`int$3#x)div 16;"0123456789ABCDEF"raze(j-16*j div 16),'a}
q)hex"Hello"
"48656C"
q)update colour:hex'[username] from table
If I create a table with:
t = table(magic(3));
I get a table with a Singular Variable Name
However if I:
a = magic(3);
T = array2table(a);
Then I get a table with Three Variable Names:
If I try to group the columns by sending it only one variable name for the table:
T.Properties.VariableNames = {'OneName'};
The VariableNames property must contain one name for each variable in the table.
In the second situation, there is an option to combine the columns into one column manually by highlighting the columns and right clicking on the mouse.
How can I programmatically group the three variables to become one Variable as in the first example if I already created the matrix a ?
EDIT:
*as in the first example if I already created the table a ?
I am using R2017b
Based on the comment below, I am asking how to do mergevars prior to R2018a.
In the above example, I would be able to group them into one variable with:
t = table(a);
In other words, I hoped to create multiple multicolumn variables. In other-other words, to do mergevars prior to R2018a.
Once the table T has been created with a variable name for each column, the column values could be extracted, and then assigned back to T:
b = T{:, 1:2};
c = T{:, 3};
T = table(b, c);
The program just selects everything if the carrid is ok even if it is not ok with the lt_spfli. And there aren't any entries with that carrid it gets runtime error. If I try with for all entries he just selects absolutely the entire SFLIGHT.
PARAMETERS: pa_airp TYPE S_FROMAIRP,
pa_carid TYPE S_CARR_ID.
DATA: lt_spfli TYPE RANGE OF SPFLI,
lt_sflight TYPE TABLE OF SFLIGHT.
SELECT CONNID FROM SPFLI
INTO TABLE lt_spfli
WHERE AIRPFROM = pa_airp.
SELECT * FROM SFLIGHT
INTO TABLE lt_sflight
WHERE CARRID = pa_carid AND CONNID in lt_spfli.
I just suppose, that you want every flight connection from a given airport...
Notice, that a RANGE structure has two more fields in front of the actual "compare value". So selecting directly into it will result in a very gibberish table.
Possible Solutions:
Selecting with RANGE
If you really want to use this temporary table, you can have a look at my answer here where I describe the way to fill RANGEs without any overhead. After this step, your current snippet will work the way to wanted it too. Just make sure, that it really has been filled or everything will be selected.
Selecting with FOR ALL ENTRIES
Before you use this variant you should make absolutely sure, that your specified data object is filled. Otherwise it will result in the same mess as the first solution. To do that, you could write:
* select connid
IF lt_spfli[] IS NOT INITIAL.
* select on SFLIGHT
ELSE.
* no result
ENDIF.
Selecting with JOIN
The "correct" approach in this case would be a JOIN like:
SELECT t~*
FROM spfli AS i
JOIN sflight AS t
ON t~carrid = #pa_carid
AND t~connid = i~connid
INTO TABLE #DATA(li_conns)
WHERE i~airpfrom = #pa_airp.
Use a FOR ALL ENTRIES instead of CONNID in lt_SPFLI.
As so:
SELECT *
FROM sflight
FOR ALL ENTRIES IN lt_spfli
WHERE carrid = pa_carid
AND connid = lt_spfli-connid
You are misunderstanding what a "Ranges Table" is. You fill it incorrectly.
This part of your code demonstrates the misunderstanding (with a little debug, you would see the erroneous contents immediately):
DATA: lt_spfli TYPE RANGE OF SPFLI.
SELECT CONNID FROM SPFLI INTO TABLE lt_spfli ...
A "Ranges Table" is an internal table with 4 components (SIGN, OPTION, LOW, HIGH), used in Open SQL to do complex selections on one database column (NB: it can also be used in several ABAP statements to test the value of an ABAP variable).
So, with your SQL statement, you only initialize the first component of the Ranges table, while you should transfer CONNID into the third component.
In "modern" Open SQL, you'd better do:
SELECT 'I' as SIGN, 'EQ' as OPTION, CONNID as LOW FROM SPFLI INTO TABLE #lt_spfli ...
For more information about Ranges Tables, you may refer to the answer here: What actually high and low means in a ranges table
With DB2 I'm able to declare anonymous custom types (e.g. row types or composite types) for my user defined functions - see the following example (especially the last line):
DB2 example:
CREATE OR REPLACE FUNCTION myFunction(IN input1 DECIMAL(5), IN input2 DECIMAL(5))
RETURNS DECIMAL(2)
READS SQL DATA
LANGUAGE SQL
NO EXTERNAL ACTION
NOT DETERMINISTIC
BEGIN
DECLARE TYPE customAnonymousType AS ROW(a1 DECIMAL(2), a2 DECIMAL(2), a3 DECIMAL(2));
/* do something fancy... */
Can I do something similar with PL/pgSQL? I know I would be able to use existing row types, also existing user defined types - but do I really have to define the type in advance?
I also know about the RECORD type, but as far as I understand I would not be able to use it in arrays (and also it would not be a well defined type).
Comments asked for an example, even though it does lengthen the question a lot I tried to define a quite simple example (still for DB2):
CREATE OR REPLACE FUNCTION myFunction(IN input1 DECIMAL(5), IN input2 DECIMAL(5))
RETURNS DECIMAL(2)
READS SQL DATA
LANGUAGE SQL
NO EXTERNAL ACTION
NOT DETERMINISTIC
BEGIN
DECLARE TYPE customAnonymousType AS ROW(a1 DECIMAL(2), a2 CHARACTER VARYING(50));
DECLARE TYPE customArray AS customAnonymousType ARRAY[INTEGER];
DECLARE myArray customArray;
SET myArray[input1] = (50, 'Product 1');
SET myArray[input2] = (99, 'Product 2');
RETURN myArray[ARRAY_FIRST(myArray)].a1;
END
This function of course only serves as a dummy function (but I suppose it is already quite long for a question here). Actually it just decides which number to return depending on if input1 is greater than input2. If input1 is smaller than input2, it returns 50, if input2 is smaller or equal than input2 it would return 99.
I know I'm not even using my a2 character field of my type (so in this case I would also be able to just use an number array) and that there are probably many, many better solutions to return two fixed numbers depending on the input values, but still my original questions remains if I am able to use anonymous custom types in PL/pgSQL (as I would in Oracle or DB2 procedures) - or if there are any similar alternatives.
You cannot to create types with local visibility in Postgres. This functionality is not supported. Postgres support global custom composite types only.
See CREATE TYPE doc. This statement cannot be used in DECLARE part of plpgsql block.
In my frontend application I have a function that is called pick(VALUE,'col1','col2','col3'). If the VALUE is 2 the value in col2 should be picked.
This is very handsome for replacing long code using "case when", "switch case" or "if else" calculations.
I have tried to find a similar function in Postgres, but no luck so far. Seen some function array() and values() mentioned, but cannot find the correct syntax.
The goal is to set an return on of three column values depending on first column value.
Pseudo code (not working):
Select status values(column1,column2,column3)from code
I know I can do this by using "case-when-then-else-end", but I am looking for a shorter way to achieve the same thing.
Jsfiddle showing the principe. But I only want to pick ONE value depending on type:
http://sqlfiddle.com/#!15/e0b41/10
You can create an array of values from pr_* columns, then pick one of them in this way:
(array[prl_1,prl_2,prl_3])[code_type]
Here is a simple demo: http://sqlfiddle.com/#!15/e0b41/23
select *,
(array[prl_1,prl_2,prl_3])[code_type]
from code
left join prl on prl_id =1