DB2 creating a random but unique character identifier upon row insert - sql

I currently have a column in a DB2 table which is being passed through web calls and procedure by a character-encrypted value. It is type CHARACTER(13) with a CSSID for encryption.
This has become a huge pain to accommodate through multiple APIs but was initially intended to allow us a unique ID to use in calls that wasn't the primary key.
In DB2-400, what would be the next best thing as far as a 13 or more character string that is unique and randomly created upon insert, but doesn't require decryption (just a plain string)?
Is there a commonly-gravitated-to method for this? We aren't passing secure data, so there's no need for encryption, but we just want a randomly created and unique character

Try hex(generate_unique()). It's unique CHAR(26) string.
Or to_char(timestamp(generate_unique()), 'YYYYMMDDHH24MISSFF6'). You may play with format of the to_char function as well. May be useful to use, let's say, reverse format like FF6SSMIHH24DDMMYYYY to avoid unique index page contention upon heavy insert activity.

This is a comment that doesn't fit in the comments section.
I don't have access to a DB2-400 (anymore), but I tested the code below in DB2 10.5 for Linux.
create sequence seq1;
select concat('A', varchar_format(next value for seq1, '000000000000')) as my_id
from sysibm.sysdummy1;
Result, if you run it 4 times in a row:
A0000000000001
A0000000000002
A0000000000003
A0000000000004
Maybe there's something equivalent in DB2-400.

Sounds like you might be using GENERATE_UNIQUE()
GENERATE_UNIQUE function returns a bit data character string 13 bytes
long (CHAR(13) FOR BIT DATA)
Doesn't really have anything to do with encryption...
And pretty much the ideal solution in my opinion generating a unique value other than a simple numeric identity. So what the problem you are having?

Related

Alternative for regexp_replace for BIGINT

I'm quite new at programming with Oracle and DB2 and have a question. I need to mask a field that has a BIGING as datatype. But when i tried to execute a query with regexp_replace, i have this error line SQLCODE=-420, SQLSTATE=22018.
Is there a alternative for a regexp_replace for BIGING.
Thanks a lot!
You can 'mask' integers by replace all digits except first and last by zeroes using next code (Oracle):
select
N, -- source number
FLOOR(N/POWER(10, FLOOR(LOG(10, N)))) * POWER(10, FLOOR(LOG(10, N))) + MOD(N, 10) MASKED
from a;
run sql online
Depending on the platform and version of Db2, you might consider using CREATE MASK if available. That would ensure the data is always masked without needing to do it in every application.
A quick search seems to indicate the Oracle also has similar support but they call it redaction. Masking in oracle seems to be tied into subsetting and exporting data from production to DEV/TEST.
Do you really need a solution for both RDBMs?
And if you really want to roll your own, you need to provide some examples of the masked value you want returned.
EDIT
Here is a part of the code. PK_PERSON has BIGINT as datatype. update
Person.T_PERSON set PK_PERSON = REGEXP_REPLACE(PK_PERSON, '[0-9]',
'*') where PK_PERSON in ('117888')
That's not going to work, you can't set a BIGINT column to a string. That's also not how masking works. Masking generally refers to a process that happens when the data is read out of the DB.

Trying to generate a unique ID that isn't an integer nor exactly other values in plaintext

I'm helping a colleague who has been asked to generate a key ID for two different groups of data coming in. I've completed this step but it's not very user friendly so I'm looking for suggestions on how to make it more readable. Each group has its own ID that appears to be a hexadecimal value. The concatenation of them appears to be a unique key in its own right.
In this case, the Household table and the Account table are being brought together and she has been asked to generate at Household-Account value (a household can have many accounts, an account can span households).
Our data is stored on SQL server but we do most of our manipulations using SAS, hence, PROC SQL below.
My initial thought was that the most obvious key is to run the two key fields together and use a delimiter. You'll see that in the top portion of my code. However this makes a very long field so I was asked to shorten it. My second thought, and their initial ask, was to just do an integer field. You can see that with the Monotonic but they felt that since it has warnings about it around the internet they don't trust it. My third thought was to run the existing, concatenated field through some kind of one-way function but when I do that (see MD5 below) I get something that looks like wingdings took over.
/* creating a table of just the "key" columns */
PROC SQL;
CREATE TABLE work.ConcatonatedKey AS
SELECT DISTINCT
CATX("G", HouseholdKey,FinancialKey) as Concatonated
FROM work.OriginalData
;
QUIT;
/* Populate HHFinancialKey */
/* Monotonic documentation */
/* http://support.sas.com/techsup/notes/v8/15/138.html */
PROC SQL;
CREATE TABLE work.ContrivedKeys AS
SELECT
Monotonic() AS HHFinID
, Concatonated
, MD5(Concatonated) As foo
FROM work.ConcatonatedKey
;
QUIT;
So, the real question here is, if you had something that could uniquely ID a row but wanted to make it more user friendly, using SAS, how would you go about it. ?
The SAS UUIDGEN function can return either human readable character string or a denser binary string. Per docs:
The UUIDGEN function returns a UUID (a unique value) for each cell. The default result is 36 characters long and it looks like:
5ab6fa40-426b-4375-bb22-2d0291f43319.
A binary result is 16 bytes long.
Example:
select
...
uuidgen() as myGroupId length=36
...
MD5 is probably the simplest solution. The MD5 function returns a 16 byte string as a result, but to make it human readable you can just format it using the $hex32. format. It's also very fast and widely supported.
data _null_;
x = put(md5("some_string_here"),$hex32.);
put x;
run;
Result:
BB28824D60AE6706F812CC940CAAAF1B
Just be careful that md5() is sensitive to case differences, and leading/trailing spaces. So you may want/need to upppercase everything and trim spaces prior to running it through the function to get consistent results across different platforms.
The risk of collisions is close to zero:
How many random elements before MD5 produces collisions?
Should also note that, knowing the two unhashed keys used to create the hash, you can recreate the hash from the keys, something that isn't possible with the uuidgen solution selected as the answer. Depending on your requirements this may or may not be a requirement.

Like operator for integer

I have a column of type bigint (ProductSerial) in my table. I need to filter the table by the Product serial using like operator. But I found that, like operator can't be used for integer type.
Is there any other method for this (I don't want to use the = operator).
If you must use LIKE, you can cast your number to char/varchar, and perform the LIKE on the result. This is quite inefficient, but since LIKE has a high potential of killing indexes anyway, it may work in your scenario:
... AND CAST(phone AS VARCHAR(9)) LIKE '%0203'
If you are looking to use LIKE to match the beginning or the end of the number, you could use integer division and modulus operators to extract the digits. For example, if you want all nine-digit numbers starting in 407, search for
phone / 1000000 = 407
Although I'm a bit late to the party, I'd like to add the method I'm using to match the first N given numbers (in the example, 123) in any numeric-type column:
SELECT * FROM MyTable WHERE MyColumn / POWER(10, LEN(MyColumn) - LEN(123)) = 123
The technique is similar to #dasblinkenlight's one, but it works regardless of the number of digits of the target column values. This is a viable workaround if your column contain numbers with different length and you don't want to use the CAST+LIKE method (or a calculated column).
For additional details on that (and other LIKE workarounds) check out this blog post that I wrote on this topic.
If you have control over the database you could add a calculated column to copy the integer value to a string:
ALTER TABLE MyTable
ADD CalcCol AS (CAST(ProductSerial AS VARCHAR)) PERSISTED
And query like:
SELECT *
FROM MyTable
WHERE ProductSerial LIKE '%2548%'
This will move the calculation to the insert/update and only on rows inserted/updated rather then converting every row for each query.
This may be a problem if there are a lot of updated to columns as it will add a very small overhead to these.
There may be a way to do it mathematically using modulus but this would take a lot of working out and testing.
You can change your Field PhoneNumbers and store as String and then use the Like You can alter your table so that you can use the LIKE statement, if you still want to use BIGint for your phone numbers, you cannot get the exact Phone Number without using = the method you can use is Between method that looks for the Numbers that are inside the range.
For the edited question: I think you should use = sign for their ID, or convert the Int to String and then Use Like.
The original question related to a phone number. OP has since edited it to refer to serial numbers. This answer refers to the original question only.
My suggestion is to avoid storing your phone numbers as integers in the first place, and thus the problem does not occur. My phone number is in the form, internationally, of:
+44 7844 51515
Storing it as an integer makes no sense here, as you will never need to do any mathematical operation on it, and you would lose the leading plus. Within the UK, it is:
07844 51515
and thus storing it as an integer would lose its leading zero. Unless you have a very very specific requirement to store it as an integer, you would fare significantly better storing it as a string instead.
[Note: Not actually my phone number]

Sql function to turn character field into number field

I'm importing data from one system to another. The former keys off an alphanumeric field whereas the latter requires a numeric integer field. I'd like to find or write a function that I can feed the alphanumeric value to and have it return a number that would be unique to the value passed in.
My first thought was to do a hash, but of course the result of any built in hashes are going to contains letters and plus it's technically possible (however unlikely) that a hash may not be unique.
My first question is whether there is anything built in to sql that I'm overlooking, and short of that I'd like to hear suggestions on the easiest way to implement such a function.
Here is a function which will probably convert from base 10 (integer) to base 36 (alphanumeric) and back again:
https://www.simple-talk.com/sql/t-sql-programming/numeral-systems-and-numbers-conversion-in-sql/
You might find the resultant number is too big to be held in an integer though.
You could concatenate the ascii values of each character of your string and cast the result as a bigint.
If the original data is known to be integers you can use cast:
SELECT CAST(varcharcol AS INT) FROM Table

Add fraction constraint to a column in oracle

I am using oracle 10gr2. I am trying to enforce a constraint on a column called "score" such that only fractions can be entered.
More specifically, a the format should contain one digit in the numerator and one digit in the denominator, such that a user can only enter a fraction such as 3/4,2/5,or 7/8. The column only accepts numbers as the input. Can anyone show me the SQL to use?
If I understand correctly, I think the proper way to do this is to store the data in two columns. This especially makes sense if the top number is a user's actual score on a problem and the bottom number is the possible score, which is what it sounds like you are doing. This will enable you to sum up scores using the built in number functions in Oracle rather than parsing strings. Then, you limit the size of each column to (0-9) by using the type NUMBER(1,0). For example:
alter table table_name add (
column possible number(1,0),
column actual number(1,0)
);
If you have data in the score column already, you then copy your values over from there to your new columns. Finally, you drop that column.
alter table table_name drop score;
Also, I'd do a search on "Oracle less than constraint", because you probably don't want the actual score to exceed the possible, and probably do a similar constraint to make the possible score greater than zero.
I don't have an instance of Oracle to test against, but here are some pointers and some untested code:
Pointers:
Look here on how to create a check constraint: http://www.techonthenet.com/oracle/check.php
After you know that, you can use Regex to validate the input. Read here for further reference: http://psoug.org/reference/regexp.html
You are probably going to use REGEX_LIKE and it would look like something like this:
ALTER TABLE your_table
add CONSTRAINT check_your_field
CHECK (REGEXP_LIKE(your_field, '^[0-9]+/[0-9]+$'));
Warning: this is not guaranteed to be fully functional code. It's a lead. Read, research and adjust accordingly.
A caveat: 1/0 will be considered valid by the Regex above, but we all know it should not be. There's a way you can know about the second part of the fraction. Read the Regex link, everything you need to know is there.
The domain of valid values is finite and small. Therefore, consider putting them all in a table, using a fixed width text column (i.e. three characters) and a create a foreign reference to this lookup table, rather than a CHECK constraint.