Overrwrite the n-th byte in a binary(256) - sql-server

How can I overwrite the n-th byte in a binary(256) using a stored procedure?
The procedure accepts two parameters:
#n tinyint -- index of byte
#value tinyint -- value to be written
I know that I can read a byte by using substring function, so I thought that I can write a byte using the stuff funtction, but it doesn't work.
I'n using SQL Server 2012.

You can use stuff, but it returns a new value, not changes the value in-place.
set #pattern = stuff(#pattern, 5, 1, 0x80); -- changes 5th byte to 0x80

Related

Update one certain bit in a bitmask field (SQL Server)

I have an int column containing 8 bits in my SQL Server database. How can I update certain bit without affecting others?
For example, I have a value
11010000
and I want to set bit1 and bit2 to 1, so it would become
11010011
Looked through bitwise operators but couldn't find proper solution.
My goal is not only to update certain bit, but also to avoid database locks.
So when transaction1 updates bit1 in a certain record, another transaction2 could update bit2 in the same field of the same record at the same time.
Is this possible? Or is using 8 separate bit columns the only way?
What you are looking for are the Bitwise operations. To always turn on the proper bit, use a bitwise OR. So, to turn on bits 1 and 2 (total value = 3), use an statement like this (assumes the value in #value is 208, or 11010000 binary):
SET #value = #value | 3
-- or, the alternate form
SET #value |= 3
Other operators are the Bitwise AND (SET #value = #value & 3), and the bitwise NOT (SET #Value = #value ~ 3), and the Bitwise XOR (SET #value = #value ^ 3).
That said, having eight bit fields is easier logically for a new programmer. I don't need to find something special to see that the field ShowCurrencySymbol is the flag for showing a currency symbol, as opposed to finding out what the fifth bit in a byte does. And, since the fields are compacted internally so that eight one-bit, non-nullable fields = one byte of space used (adding NULL takes two bits per bit).
Finally, you can't have two transactions simultaneously update a field on the same row. While one update is occurring, the row will be locked, preventing the other update from processing. If you really want something like this, you will have to use a much more expansive method - a separate BitValues or Flags table, something like this:
CREATE TABLE Flags (
RowID int not null,
FlagName varchar(16) Not Null,
BitValue bit not null,
CONSTRAINT PK_Flags PRIMARY KEY (RowID, FlagName)
);
Then you read and write your flags from this table separate from the row.
A pseudo-bitset value is when you have an integer or string that, when selected or printed, looks like a bitset. Something like this:
DECLARE #bits varchar(8) = '1010'
SELECT Right(8, REPLICATIE('0', 8) + #bits
Value returned: 00001010
For this type of bitset, you need to remember that you are not setting the bits, but are representing the bits instead, and you need to set the representation of the bits in the same way. For example, the function below can be used to set bits in a string representation of your bitstring:
CREATE FUNCTION dbo.SetBitInString
(
#Source char(8),
#Position int,
#Action char(1) = 'S',
#Value bit = NULL
)
RETURNS Char(8)
AS
BEGIN
DECLARE #work Char(8) = '00000000'
SET #work = Right(#work + ISNULL(LTRIM(RTRIM(#Source)), ''), 8)
SET #Work =
CASE #Action
WHEN 'S'-- Set
THEN Stuff(#Work, #Position, 1, '1')
WHEN 'R' -- Reset
THEN Stuff(#Work, #Position, 1, '0')
WHEN 'X' -- XOR value with position
THEN STUFF(#Work, #Position, 1, CAST(CAST(SubString(#Work, #Position, 1) as int) ^ ISNULL(#Value, 0) as CHAR(1)))
WHEN 'Q'
THEN '1'
---- add other options as needed - this is a quick example.
ELSE #Action
END
IF (#Action = #Work)
RAISERROR('Bad Action (%s) Submitted', 13, 1, #Action)
RETURN #Work
END
Reading a bit is a simple SUBSTRING. Constants can be defined for the meaning of each bit (e.g. DECLARE #ShowTotalPrice = 4 -- The 4th bit is the Show Total Price flag)
This should give you enough to go on if you want to use this style of setting, where the displayed value is your bitset represented in 1s and 0s.

SQL Server encryptbycert capping off

My problem is that I'm trying to encrypt a column in a SQL Server database because of policies of my work place. I have access only to simple methods for encrypting (TDE seems out of my possibilities) so I've tried using EncryptByCert or EncryptByKey. I was doing fine since the documentation shows the cap at 8000 which is enough for the data we're saving.
It just so happens that when I try to save anything it caps off at around 200 characters generating a 514 byte long varbinary. The 514 byte length varbinary will encrypt and decrypt fine but will not grow or shorten, a single character counts the same as a 200 string making those same 514 bytes binary. After say around 230 characters that I want to encrypt it will just leave the column null.
Does anyone know what's happening with that?
Encryption performed by these methods is done in chunks, with the maximum chunk size is the key length minus some internal overhead (117 bytes for 1024 bit keys, and 245 bytes for 2048 bit keys first introduced in SQL Server 2016).
If your input is any larger than that, you have to split it into chunks and encrypt one at a time, then concatenate the result.
Decryption, of course, should be performed accordingly. However, an important difference between the two is that encryption chunk size will be smaller than the key, and for decryption it should be exactly the key size. That's because any data, however short, will be encrypted into key long chunk, so that no guesses on the input length can be made by looking at the output.
Here is an excerpt from my encryption function (written for 2012 version, so 1024 bit keys are assumed):
create function [dbo].[security_EncryptByCert]
(
#ClearText varbinary(max)
)
returns varbinary(max) with schemabinding, returns null on null input as begin
-- Returned value
declare #Ret varbinary(max) = 0x,
-- Length of the ciphertext
#Lng int = datalength(#ClearText),
-- Starting offset of the chunk to encrypt
#i int = 1,
-- Chunk size, currently it can't be more than 117 bytes
#Size int = 100,
-- Certificate to encrypt data with
#CertId int;
-- Determine the certificate with which to perform encryption
select #CertId = Id from ...
-- Iterate chunk by chunk til the end of the text
while #i < #Lng begin
set #Ret += encryptbycert(#CertId, substring(#ClearText, #i, #Size));
-- Move the pointer to the next block
set #i += #Size;
end;
return #Ret;
end;
In this case, I used 100 byte chunks, not the largest possible ones. Don't really remember why, but you can use 245 bytes as a limit on 2016.

I want to compare alphanumeric strings in same column in SQL Server

I have a table with details of family members staying in a particular locality. Since these are government data, it has lot mistakes. Like in one column 'houseno', there a 2 values 'Ti 303' and '303' which are same house numbers.
In the end, I want Ti 303 to be updated with '303'. (As these are family members living in same house)
Similarly 'P-101' and 'P/101' are same houseno's and I want it to be converted to either 'P-101' or 'P/101'. I tried difference, substring etc but of now use to me. Please help!
You just need to strip out the characters to compare the content?
CREATE FUNCTION dbo.FN_GetNumberPart (#strMixedString VARCHAR(200))
RETURNS VARCHAR(200)
AS
BEGIN
DECLARE #NumberPart INT
-- Get the next non numeric character position
SET #NumberPart = PATINDEX('%[^0-9]%', #strMixedString)
-- While there are non numeric characters remaining
WHILE #NumberPart > 0
BEGIN
-- Remove the non numeric character from the string
SET #strMixedString = STUFF(#strMixedString, #NumberPart , 1, '' )
-- Get the next non numeric character position
SET #NumberPart = PATINDEX('%[^0-9]%', #strMixedString)
END
-- Spit out the cleansed string
RETURN ISNULL(#strMixedString,0)
END
GO
SELECT dbo.FN_GetNumberPart(HouseNo)
from TblAddresses
You should use the REPLACE command. For the two examples give you could hard code it as follows:
select REPLACE('Ti 303','Ti ','')
select REPLACE('P-101','P-','P/')
You would use REPLACE in your UPDATE command and not as a SELECT obviously.
If you have a list of strings to replace in a column with an update then you could put these into a table. Then use this in your REPLACE command for the string pattern to be replaced.

Can I use a hash of fields instead of direct field comparison to simplify comparison of records?

I am integrating between 4 data sources:
InternalDeviceRepository
ExternalDeviceRepository
NightlyDeviceDeltas
MidDayDeviceDeltas
Changes flow into the InternalDeviceRepository from the other three sources.
All sources eventually are transformed to have the definition of
FIELDS
=============
IdentityField
Contract
ContractLevel
StartDate
EndDate
ContractStatus
Location
IdentityField is the PrimaryKey, Contract Key is a secondary Key only if a match exists, otherwise a new record needs to be created.
Currently I compare all the fields in a WHERE clause in SQL Statements and also in a number of places in SSIS packages. This creates some unclean looking SQL and SSIS packages.
I've been mulling computing a hash of ContractLevel, StartDate, EndDate, ContractStatus, and Location and adding that to each of the input tables. This would allow me to use a single value for comparison, instead of 5 separate ones each time.
I've never done this before, nor have I seen it done. Is there a reason that it should be used, or is that a cleaner way to do it?
It is a valid approach. Consider to introduce a calculated field with the hash and index on it.
You may use either CHECKSUM function or write your own hash function like this:
CREATE FUNCTION dbo.GetMyLongHash(#data VARBINARY(MAX))
RETURNS VARBINARY(MAX)
WITH RETURNS NULL ON NULL INPUT
AS
BEGIN
DECLARE #res VARBINARY(MAX) = 0x
DECLARE #position INT = 1, #len INT = DATALENGTH(#data)
WHILE 1 = 1
BEGIN
SET #res = #res + HASHBYTES('MD5', SUBSTRING(#data, #position, 8000))
SET #position = #position+8000
IF #Position > #len
BREAK
END
WHILE DATALENGTH(#res) > 16 SET #res= dbo.GetMyLongHash(#res)
RETURN #res
END
which will give you 16-byte value - you may take all the 16 bytes as Guid, or only first 8-bytes as bigint and compare it.
Adapt the function in your way - to accept string as parameter or even all the your fields instead of varbinary
BUT
be careful with strings casing, datetime formats
if using CHECKSUM - check also other fields, checksum produces dublicates
avoid using 4-byte hash result on relaively big table

Check for Numeric Value using SQL Server 2000

How can I know if a VARCHAR field's value can be successfully converted to an integer?
I want to do it massively to insert records from one table to another...
IsNumeric() function returns 1 for strings (varchars) which can be converted to a number and 0 for those that cannot..
Check out IsNumeric function
One issue whit IsNumeric() function is that You will get True and if number got decimal separator,
What is totally right, But if someone as I need to check straight to numbers in varchar, without decimal symbols, (I got that when I needed to calculate CHECK digit on barcode) You can use castom
made function like
create FUNCTION [dbo].[checkbarkod]
(
#ean_kod varchar(13)
)
RETURNS bit
AS
begin
declare #duzina int
declare #slovo char(1)
declare #pozicija int
declare #uredu bit
set #duzina=len(#ean_kod)
while #duzina>0
begin
set #slovo=(substring(#ean_kod,#duzina,1))
if (#slovo not in('1','2','3','4','5','6','7','8','9','0'))
begin
set #uredu=convert(bit,0)
break
end
else
begin
set #uredu=convert(bit,1)
set #duzina=#duzina-1
end
end
RETURN #uredu
end

Resources