Case: We have smart guids in a table and need to extract 2nd and 4th parts out of it. I was thinking about writing a function that can take in #partnumber and return the extracted value for it.
e.g.
DECLARE #Guid UNIQUEIDENTIFIER = 'A7DDAA60-C33A-4D7A-A2D8-ABF20127C9AE'
1st part = A7DDAA60, 2nd part = C33A, 3rd part = 4D7A, 4th part =
A2D8, and 5th part = ABF20127C9AE
Based on the #partnumber, it would return one of those values.
I'm trying to figure out how to split it most efficiently (STRING_SPLIT doesn't guarantee order).
I am not sure exactly what you mean by "smart" guids, but why not just cast it to a char and pull out the parts by position?
create table t(myguid uniqueidentifier);
declare #p tinyint = 5;
select case #p
when 1 then left(c.v, 8)
when 2 then substring(c.v, 10, 4)
when 3 then substring(c.v, 15, 4)
when 4 then substring(c.v, 20, 4)
when 5 then right(c.v, 12)
end
from t
cross apply (select cast(t.myguid as char(36))) c(v)
You can use, OPENJSON
DECLARE #Guid UNIQUEIDENTIFIER = 'A7DDAA60-C33A-4D7A-A2D8-ABF20127C9AE',
#s varchar(100)
Select #s = replace(#guid,'-','","')
Select * from
(
Select [key] + 1 as Poistion, Value as Part
FROM OPENJSON('["' + #s + '"]')
) Q
Where Poistion in (2,4)
Here is the fiddle.
It's pretty hard topic for me because SQL is not my best skill ;)
I must insert random hex colors into database row. How can I do it? Is it possible to create function that will draw numbers?
SET [Color] = '#' + CONVERT(VARCHAR(max), CRYPT_GEN_RANDOM(3), 2)
Here is the logic explained below wrapped in a function for MySQL. It's very easy to use.
mysql> select random_color();
+----------------+
| random_color() |
+----------------+
| #8F50B4 |
+----------------+
1 row in set (0.00 sec)
It can be called over and over again and each time it will have a different color.
CREATE FUNCTION `random_color`() RETURNS char(7) CHARSET latin1 DETERMINISTIC
BEGIN
DECLARE str CHAR(7);
SET str = concat('#',SUBSTRING((lpad(hex(round(rand() * 10000000)),6,0)),-6));
RETURN str;
END;
This will give you six digit hex number codes in MySQL
SELECT concat('#',SUBSTRING((lpad(hex(round(rand() * 10000000)),6,0)),-6))
Here's a great one that will give you incremental colors
SELECT *,
concat('#',SUBSTRING((lpad(hex(#curRow := #curRow + 10),6,0)),-6)) AS color
FROM table
INNER JOIN (SELECT #curRow := 5426175) color_start_point
This works for me in MySQL:
mysql> SELECT CONCAT('#',LPAD(CONV(ROUND(RAND()*16777215),10,16),6,0)) AS color;
+---------+
| color |
+---------+
| #0E74A9 |
+---------+
1 row in set (0.00 sec)
Short explanation:
16777215 is the maximum 24 bit unsigned integer, that is: 224-1
Multiplied by RAND() and ROUND()'ed gives a random unsigned integer in the RGB color range: (0, 224-1)
Then, convert from base 10 to base 16 to get hexadecimal
LPAD() to zero-fill by left
And finally, CONCAT() to get the '#' character
with cte1 as (
select round(round(rand(),1)*15,0) as hex1,
round(round(rand(),1)*15,0) as hex2,
round(round(rand(),1)*15,0) as hex3,
round(round(rand(),1)*15,0) as hex4,
round(round(rand(),1)*15,0) as hex5,
round(round(rand(),1)*15,0) as hex6
),
cte2 as (
select case when hex1 = 10 then 'A'
when hex1 = 11 then 'B'
when hex1 = 12 then 'C'
when hex1 = 13 then 'D'
when hex1 = 14 then 'E'
when hex1 = 15 then 'F'
else str(hex1) end as hex1h,
case when hex2 = 10 then 'A'
when hex2 = 11 then 'B'
when hex2 = 12 then 'C'
when hex2 = 13 then 'D'
when hex2 = 14 then 'E'
when hex2 = 15 then 'F'
else str(hex2) end as hex2h,
case when hex3 = 10 then 'A'
when hex3 = 11 then 'B'
when hex3 = 12 then 'C'
when hex3 = 13 then 'D'
when hex3 = 14 then 'E'
when hex3 = 15 then 'F'
else str(hex3) end as hex3h,
case when hex4 = 10 then 'A'
when hex4 = 11 then 'B'
when hex4 = 12 then 'C'
when hex4 = 13 then 'D'
when hex4 = 14 then 'E'
when hex4 = 15 then 'F'
else str(hex4) end as hex4h,
case when hex5 = 10 then 'A'
when hex5 = 11 then 'B'
when hex5 = 12 then 'C'
when hex5 = 13 then 'D'
when hex5 = 14 then 'E'
when hex5 = 15 then 'F'
else str(hex5) end as hex5h,
case when hex6 = 10 then 'A'
when hex6 = 11 then 'B'
when hex6 = 12 then 'C'
when hex6 = 13 then 'D'
when hex6 = 14 then 'E'
when hex6 = 15 then 'F'
else str(hex6) end as hex6h from cte1)
select '#'+ltrim(hex1h)+ltrim(hex2h)+ltrim(hex3h)+ltrim(hex4h)+ltrim(hex5h)+ltrim(hex6h) from cte2
Here is a SQL Server Function which works in all versions from 2012 onwards that generates random HTML colour codes in hex format, i.e. #AABB00. It requires a View as well, which is also provided below.
The Function gives you the option to avoid light or dark colours, which can be useful if you are trying to generate background and foreground colours that won't clash too badly (i.e. dark text on a dark background).
CREATE FUNCTION dbo.Get_Random_Colour (#intStyle tinyint = 0)
RETURNS varchar(7)
/*
* Purpose: Returns the HTML colour code for a random colour
* Inputs: #intStyle - 0: does not filter the colour range
* 1: avoid dark colours
* 2: avoid light colours
*/
AS
BEGIN
DECLARE #c1 char(2), #c2 char(2), #c3 char(2)
DECLARE #i1 tinyint, #i2 tinyint, #i3 tinyint
DECLARE #strResult As varchar(255)
DECLARE #intLow tinyint = 0
DECLARE #intHigh tinyint = 255
IF #intStyle = 1
SET #intLow = 80
IF #intStyle = 2
SET #intHigh = 140
--Generate random numbers
SELECT #i1 = CAST(ROUND((#intHigh-#intLow) * RandNumber + #intLow,0) as int) from dbo.vRandNumber
SELECT #i2 = CAST(ROUND((#intHigh-#intLow) * RandNumber + #intLow,0) as int) from dbo.vRandNumber
SELECT #i3 = CAST(ROUND((#intHigh-#intLow) * RandNumber + #intLow,0) as int) from dbo.vRandNumber
--Convert them to hex format
SELECT #c1 = FORMAT(#i1, 'X')
SELECT #c2 = FORMAT(#i2, 'X')
SELECT #c3 = FORMAT(#i3, 'X')
--Pad them to two characters
SELECT #strResult = '#'
+ REPLICATE('0', 2-LEN(#c1)) + #c1
+ REPLICATE('0', 2-LEN(#c2)) + #c2
+ REPLICATE('0', 2-LEN(#c3)) + #c3
RETURN #strResult
END
GO
And here is the View that the Function is dependent on:
CREATE VIEW [dbo].[vRandNumber]
AS
SELECT RAND() as RandNumber
I'm not sure which DBMS are you using, but this solution is applied on SQL Server, and could be applied on a different DBMS if you know the corresponding syntax and functions.
(I will try to be as brief as I can in this)
Hex colors are basically a hexadecimal VARBINARY values which is supported by almost all DBMS. You only need the right conversion to bind it to your needs.
For example, if we cast a string foo as VARBINARY it'll return the value of 0x666F6F and if we cast back to VARCHAR it'll return foo
SELECT CAST('foo' AS VARBINARY)
-- Returns : 0x666F6F
SELECT CAST(0x666F6F AS VARCHAR)
-- Returns : foo
So, it's the same value with corresponded datatype even if the output is different. This is also applied in colors (hex to rgb, rgb to hex, hex to binary and so on).
Now, you know that Hex colors are using VARBINARY datatype, which will be our target in this solution.
The first thing is to generate that VARBINARY by using RGB values (which is another datatype of INT values). RGB values are three different values representing Red, Green, and Blue. The minimum number of each of them is 0 and the max is 255. So, if we do RGB(0,0,0) this will return #000000 (black) and RGB(255,255,255) returns #FFFFFF (white).
Oh, I forgot, you don't need any special equation in this conversion since DBMS is already handling this.
So, your first target is to do a function which takes RGB values (with min 0 and max 255) and then convert it to Hexadecimal VARBINARY, which will gives you the Hex color.
To Convert from RGB to Hex, you will need to know each 2 hexadecimal represent an RGB Value. For instance #BA55D3 is corresponded to rgb(186,85,211)
The actual hexadecimal for it is
R = 0xBA
G = 0x55
B = 0xD3
WHERE
0xBA = 186
0x55 = 18
0xD3 = 211
. So, from each Hex color, you'll need to divide it into 3 groups of two values to represent RGB values.
If you cast 0xBA as INT it'll give you 186 which is the value of the red part.
SELECT
CAST(0xBA AS INT)
-- Returns : 186
the same thing applies to the rest.
Now, we need our function to do the work for us, for this, I have created a function that simply takes three int values and convert them into VARBINARY which helps me to convert it back to varchar to just format the output into a hex color value.
CREATE FUNCTION RGBToHex
(
#R INT,
#G INT,
#B INT
)
RETURNS VARCHAR(7)
AS
BEGIN
DECLARE
#VarR VARBINARY,
#VarG VARBINARY,
#VarB VARBINARY,
#Result VARCHAR(7)
SELECT
#VarR = CONVERT(VARBINARY, #R) * 1 ,
#VarG = CONVERT(VARBINARY, #G) * 1 ,
#VarB = CONVERT(VARBINARY, #B) * 1
SET #Result = '#' + SUBSTRING(Convert(VARCHAR(MAX),#VarR, 1), 3, 2) + SUBSTRING(Convert(VARCHAR(MAX),#VarG, 1), 3, 2) + SUBSTRING(Convert(VARCHAR(MAX),#VarB, 1), 3, 2)
RETURN #Result
END
Example :
SELECT
RGBToHex(186,85,211)
-- Returns : #BA55D3
Now, the function is ready to be used, all we need is a way to randomize three INTs inside this function, to get a random color each time, taking the minimum and maximum (0-255) values for RGB in mind.
IN SQL Server I used a recursive query for the sake of simplicity, you could do your own query with your own method or function.
;WITH CTE AS (
SELECT
ABS(CHECKSUM(NewId())) % 256 AS R ,
ABS(CHECKSUM(NewId())) % 256 AS G ,
ABS(CHECKSUM(NewId())) % 256 AS B
)
SELECT
dbo.RGBToHex(R,G,B)
FROM CTE
In the query above I have set the random number between 0-255 for each column, then I just put these values inside the function to generate a new color.
If you need a reversed function that takes color's hexadecimal and return RGB value, you could use this function :
CREATE FUNCTION HexToRGB
(
#Hex VARCHAR(7)
)
RETURNS VARCHAR(100)
AS
BEGIN
DECLARE
#VarH VARCHAR(6),
#R INT,
#G INT,
#B INT,
#Result VARCHAR(100)
SET #VarH = (CASE WHEN LEFT(#Hex, 1) = '#' THEN SUBSTRING(#Hex, 2,6) ELSE #Hex END)
SELECT
#R = CONVERT(INT, CONVERT(VARBINARY, '0x' + SUBSTRING(#VarH, 1,2), 1) ),
#G = CONVERT(INT, CONVERT(VARBINARY, '0x' + SUBSTRING(#VarH, 3,2), 1) ),
#B = CONVERT(INT, CONVERT(VARBINARY, '0x' + SUBSTRING(#VarH, 5,2), 1) )
SET #Result = 'rgb(' + CAST(#R AS VARCHAR(3) ) + ',' + CAST(#G AS VARCHAR(3) ) + ',' + CAST(#B AS VARCHAR(3) ) + ')'
RETURN #Result
END
If you are using MySQL
select concat("#", conv(round(rand() * 255), 10, 16), conv(round(rand() * 255), 10, 16), conv(round(rand() * 255), 10, 16));
Here's a solution for PostgreSQL.
SELECT concat('#',lpad(to_hex(round(random() * 10000000)::int4),6,'0'))
I used it to set the random color to Color field of the table using that code:
UPDATE "MyTable" SET "Color" = concat('#',lpad(to_hex(round(random() * 10000000)::int8),6,'0'));
Guess you need to use rand - https://dev.mysql.com/doc/refman/5.0/en/mathematical-functions.html#function_rand - i.e.
INSERT INTO ... VALUES ( ... , ROUND(RAND(255 * 255 * 255)), ...)
For all SQL queries(MySql, SQL Server, etc) this method will work
In sql we can generate random colors using the substring function. Keep in mind that in sql charecter index starts from 1 (not 0 like javascript)
select concat('#',substring('0123456789ABCDEF',round(rand() * 15)+1,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1));
Where round(rand() * 15) gives a random number between 0 and 15 including 0 and 15. Thats why we are adding 1 to it to get 1 as minimum value for the substring index
To get only Light Colors :-
select concat('#',substring('0123456789ABCDEF',round(rand() * 8)+8,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1),substring('0123456789ABCDEF',round(rand() * 8)+8,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1),substring('0123456789ABCDEF',round(rand() * 8)+8,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1));
To get only Dark Colors :-
select concat('#',substring('0123456789ABCDEF',round(rand() * 7)+1,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1),substring('0123456789ABCDEF',round(rand() * 7)+1,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1),substring('0123456789ABCDEF',round(rand() * 7)+1,1),substring('0123456789ABCDEF',round(rand() * 15)+1,1));
Update:-
In all the above cases we can use a temporary variable to have the repeating range value to minimize the query like
set #r='0123456789ABCDEF';
select concat('#',substring(#r,round(rand() * 15)+1,1),substring(#r,round(rand() * 15)+1,1),substring(#r,round(rand() * 15)+1,1),substring(#r,round(rand() * 15)+1,1),substring(#r,round(rand() * 15)+1,1),substring(#r,round(rand() * 15)+1,1));
I have the following query:
declare #vlength varchar(50)
set #vLength =
(select
CASE
WHEN
SubString(SPSYS08.length, 0, CharIndex('"', SPSYS08.length)) > 0
AND SPSYS08.um = 'PC'
THEN SubString(SPSYS08.length, 0, CharIndex('"', SPSYS08.length)) / 12
else 1
end
From
SPSYS08 Inner Join
SPSYS07 On SPSYS08.loc_code = SPSYS07.loc_code And SPSYS08.so_no =
SPSYS07.so_no)
Select
SPSYS08.loc_code,
SPSYS08.so_no,
SPSYS08.line_no,
SPSYS07.invoice_date
From
SPSYS08 Inner Join
SPSYS07 On SPSYS08.loc_code = SPSYS07.loc_code And SPSYS08.so_no =
SPSYS07.so_no
Where
SPSYS07.invoice_date = '20131206'
We are running on SQL server 2008. What I am trying to do is assign the length of a part to the vLength variable. Length is a char as it contains the inches (") symbol. I either get conversion failed when converting the varchar value to int or a select statement that assigns a value to a variable must not be combined with data-retrieval options.
How can I get around this?
Use a CTE for this instead:
;WITH Lengths AS
(select
Code,
'L' =
CASE
WHEN
SubString(SPSYS08.length, 0, CharIndex('"', SPSYS08.length)) > 0
AND SPSYS08.um = 'PC'
THEN SubString(SPSYS08.length, 0, CharIndex('"', SPSYS08.length)) / 12
else 1
end
From
SPSYS08 Inner Join
SPSYS07 On SPSYS08.loc_code = SPSYS07.loc_code And SPSYS08.so_no =
SPSYS07.so_no)
Then you can
...
JOIN Lengths L
ON L.Code = SomeOtherTable.Code
...
and get the length for each record.
First of all this part should change
THEN SubString(SPSYS08.length, 0, CharIndex('"', SPSYS08.length)) / 12
the substring returns a string which you are trying to devide by 12 which will fail
you should probably use:
THEN CAST(SubString(SPSYS08.length, 0, CharIndex('"', SPSYS08.length)) AS INTEGER) / 12
but still you should make sure your substring returns a calue that is conveertable to integer
and second, it is possible that your select statement returns multiple rows which will cause your SET to fail, so you need to SELECT TOP 1 or something in order to fix that part.
Is there a neat way to apply a mask to a string in a SQL Server query?
I have two tables, one with Phone number stored as varchar with no literals 0155567890 and a phone type, which has a mask for that phone number type: (##) #### ####
What is the best way to return a string (for a merge Document) so that the query returns the fully formatted phone number:
(01) 5556 7890
As noted in the comment, my original answer below will result in terrible performance if used in a large number of rows. i-one's answer is preferred if performance is a consideration.
I needed this also, and thanks to Sjuul's pseudocode, I was able to create a function to do this.
CREATE FUNCTION [dbo].[fx_FormatUsingMask]
(
-- Add the parameters for the function here
#input nvarchar(1000),
#mask nvarchar(1000)
)
RETURNS nvarchar(1000)
AS
BEGIN
-- Declare the return variable here
DECLARE #result nvarchar(1000) = ''
DECLARE #inputPos int = 1
DECLARE #maskPos int = 1
DECLARE #maskSign char(1) = ''
WHILE #maskPos <= Len(#mask)
BEGIN
set #maskSign = substring(#mask, #maskPos, 1)
IF #maskSign = '#'
BEGIN
set #result = #result + substring(#input, #inputPos, 1)
set #inputPos += 1
set #maskPos += 1
END
ELSE
BEGIN
set #result = #result + #maskSign
set #maskPos += 1
END
END
-- Return the result of the function
RETURN #result
END
Just in case someone ever needs a table-valued function.
Approach 1 (see #2 for a faster version)
create function ftMaskPhone
(
#phone varchar(30),
#mask varchar(50)
)
returns table as
return
with ci(n, c, nn) as (
select
1,
case
when substring(#mask, 1, 1) = '#' then substring(#phone, 1, 1)
else substring(#mask, 1, 1)
end,
case when substring(#mask, 1, 1) = '#' then 1 else 0 end
union all
select
n + 1,
case
when substring(#mask, n + 1, 1) = '#' then substring(#phone, nn + 1, 1)
else substring(#mask, n + 1, 1)
end,
case when substring(#mask, n + 1, 1) = '#' then nn + 1 else nn end
from ci where n < len(#mask))
select (select c + '' from ci for xml path(''), type).value('text()[1]', 'varchar(50)') PhoneMasked
GO
Then apply it as
declare #mask varchar(50)
set #mask = '(##) #### ####'
select pm.PhoneMasked
from Phones p
outer apply ftMaskPhone(p.PhoneNum, #mask) pm
Approach 2
I'm going to leave the above version for historical purposes. However, this one has better performance.
CREATE FUNCTION dbo.ftMaskPhone
(
#phone varchar(30),
#mask varchar(50)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH v1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
v2(N) AS (SELECT 1 FROM v1 a, v1 b),
v3(N) AS (SELECT TOP (ISNULL(LEN(#mask), 0)) ROW_NUMBER() OVER (ORDER BY ##SPID) FROM v2),
v4(N, C) AS (
SELECT N, ISNULL(SUBSTRING(#phone, CASE WHEN c.m = 1 THEN ROW_NUMBER() OVER (PARTITION BY c.m ORDER BY N) END, 1), SUBSTRING(#mask, v3.N, 1))
FROM v3
CROSS APPLY (SELECT CASE WHEN SUBSTRING(#mask, v3.N, 1) = '#' THEN 1 END m) c
)
SELECT MaskedValue = (
SELECT c + ''
FROM v4
ORDER BY N
FOR XML PATH(''), TYPE
).value('text()[1]', 'varchar(50)')
);
GO
Schema binding, in combination with this being a single-statement table-valued-function, makes this version eligible for inlining by the query optimizer. Implement the function using a CROSS APPLY as in the example above, or for single values, like this:
SELECT *
FROM dbo.ftMaskPhone('0012345678910', '### (###) ###-####')
Results look like:
MaskedValue
001 (234) 567-8910
This is just what came up in my head. I don't know whether it's the best solution but I think it should be workable.
Make a function with the name applyMask (orso)
Pseudocode:
WHILE currentPosition < Length(PhoneNr) AND safetyCounter < Length(Mask)
IF currentSign = "#"
result += Mid(PhoneNr, currentPosition, 1)
currentPosition++
ELSE
result += currentSign
safetyCounter++
END
END
Return result
As noted by #Sean, SQL Server 2012 and up supports the FORMAT function, which almost gives you what you need, with the following caveats:
It takes a number to format, rather than a VARCHAR. This could be worked around by using a CAST.
The mask as provided ((##) #### ####), coupled with a CAST would remove the leading zero, leaving you with (1) 5556 7890. You could update the mask to (0#) #### ####. Going on a limb that you're representing an Australian phone number, it seems that the leading 0 is always there anyways:
Within Australia, to access the "Number" of a landline telephone in an "Area" other than that in which the caller is located (including a caller using a "Mobile" 'phone), firstly it is necessary to dial the Australian "Trunk Access Code" of 0 plus the "Area" code, followed by the "Local" Number. Thus, the "Full National Number" (FNN) has ten digits: 0x xxxx xxxx.
But ultimately, I would argue that SQL Server is not the best place to handle representation/formatting of your data (as with dates, so with phone numbers). I would recommend doing this client-side using something like Google's libphonenumber. When a phone number is entered into the database, you could store the phone number itself and the country to which it belongs, which you could then use when displaying the phone number (or doing something like calling it or checking for validity).
There is the built in FORMAT function, which almost works. Unfortunately it takes an int as the first parameter, so it strips off the leading zero:
select format(0155567890 ,'(##) #### ####')
(1) 5556 7890
If you need to "mask", rather hide the real value with another, and then "unmask" a string you can try this function, or extend it for that matter. :)
https://stackoverflow.com/a/22023329/2175524
I wanted to hide some information, so i used RIGHT function. It shows only first 4 chars from right side.
CONCAT('xxx-xx-', RIGHT('03466045896', 4))
Above code will show "xxx-xx-5896"