I need to calculate a new column using moving calculations.
For example, I have a table:
A
B
10
15
11
14
12
13
I need to calculate new column where the 1st value is calculated like 5000/10*15, the 2nd value is (5000 / 10 * 15) / 11 * 14, the 3rd one is ((5000 / 10 * 15) / 11 * 14) / 12 * 13 and so on. Where 5000 is a random value and in the future I will use it like a parameter in a stored procedure.
I know, that in Excel for example we can reffer to the previous calculated cell. How can it be calculated using SQL?
Thank you!
create table #test (A int,B int)
insert into #test values(10,15),(11,14),(12,13)
declare #seed int=5000;
;with temp as (
select A,B,row_number() over(order by a) rn from #test
),
cte as
(
select #seed/A*B calculated,rn from temp where rn=1
union all
select c. calculated/t.A*t.B,t.rn from temp t
join cte c on t.rn=c.rn+1
)
select * from cte
There is a warning in the docs that reads:
If there are multiple assignment clauses in a single SELECT statement,
SQL Server does not guarantee the order of evaluation of the
expressions. Note that effects are only visible if there are
references among the assignments.
It means there is no guarantee that it will evaluate the expression left-to-right. For this code:
declare #a int, #b int;
select #a = 2, #b = #a * 3;
select #a, #b;
The result could be 2, 6 (#a = ... evaluated first) or 2, NULL (#b = ... evaluated first).
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));
Against SQL Server, I'm essentially trying to calculate a value based on Year to Date, so I want to sum any values from July 16, 2012 and prior and display them. I'm using the following query (note that I've replaced parameters with simple integers to calculate the value for today):
SELECT SUM(CASE
WHEN (
(
dns.ODAY <= 16
AND (dns.fiscalyear + 1) = 13
AND dns.omonth = 7
)
OR
(
(dns.fiscalyear + 1) = 13
AND dns.omonth < 7
)
)
THEN dns.QtyShipped
ELSE 0
END) AS Shipped_Units
FROM myTable dns
However, this query is returning 0 for all rows. If I replace dns.QtyShipped with an integer, say 1, it still returns 0. So obviously the case statement isn't being evaluated correctly. Is my logic flawed? Or is it a syntax issue (e.g. I need more parentheses)?
Thanks!
Additional comments:
To test, I've ran the following query:
SELECT SUM(dns.QtyShipped)
FROM myTable dns
where
(dns.ODAY <= 16
AND (dns.fiscalyear + 1) = 13
AND dns.omonth = 7)
OR
((dns.fiscalyear + 1) = 13
AND dns.omonth < 7)
Which returns a very large number. This is confusing.
The code that you mentioned earlier is working absolutely fine. Please double check the values you are using to evaluate the conditions. For example, please confirm if for fiscalyear the value is 2013 or 13. I've used variables instead of column names in the code mentioned below and its returning the expected results:
declare #ODAY integer
set #ODAY=17
declare #fiscalyear int
set #fiscalyear=12
declare #omonth int
set #omonth=8
SELECT SUM(CASE
WHEN (
(
#ODAY <= 16
AND (#fiscalyear + 1) = 13
AND #omonth = 7
)
OR
(
(#fiscalyear + 1) = 13
AND #omonth < 7
)
)
THEN 1
ELSE 0
END) AS Shipped_Units
If I had to guess I would say that your year is being stored as 4 digits. At least that is the problem I ran into when I set up my test.
When I set up this test it worked:
CREATE TABLE myTable (fiscalyear int, omonth int, ODAY int, qtyshipped int)
INSERT INTO myTable VALUES (2012,1,1,1),
(12,1,1,1),
(12,2,1,1),
(12,3,1,1),
(12,4,1,1),
(13,1,1,1),
(12,7,1,1)
When I set up this test it failed:
CREATE TABLE myTable (fiscalyear int, omonth int, ODAY int, qtyshipped int)
INSERT INTO myTable VALUES (2012,1,1,1),
(2012,1,1,1),
(2012,2,1,1),
(2012,3,1,1),
(2012,4,1,1),
(2013,1,1,1),
(2012,7,1,1)
Is there any reason you aren't using actual dates? Your logic would be much simpler and if the dates are stored in your table then the query would probably be faster too.
EDIT: Here is an additional test you can run to be sure its your case causing the problem:
SELECT SUM(CASE
WHEN (
(
dns.ODAY <= 16
AND (dns.fiscalyear + 1) = 13
AND dns.omonth = 7
)
OR
(
(dns.fiscalyear + 1) = 13
AND dns.omonth < 7
)
)
THEN 0
ELSE dns.QtyShipped
END) AS Shipped_Units
FROM myTable dns
Basically flip the case around. Return 0 if you are true and the QtyShipped if not. If you get a value this way then the problem is in your case, if you don't then the problem is probably somewhere else in your query.
I want to split every digit in an integer field in an sql table . example of that would be:
financialNb = 7869
i need the 7 as first digit, 8 as second, 6 as third and 9 as fourth. this is needed because I want every digit to be used as 1 data field in a crystal report?
First you have to convert or cast numbers into text before you can manipulate them in this way. The functions you are looking for are CAST() and SUBSTRING(). To get the numbers to start from the right, you can use REVERSE().
Try this example:
SELECT 7869 AS field1
INTO #tmp
SELECT SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),8,1) AS [Column8]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),7,1) AS [Column7]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),6,1) AS [Column6]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),5,1) AS [Column5]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),4,1) AS [Column4]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),3,1) AS [Column3]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),2,1) AS [Column2]
,SUBSTRING(REVERSE(CAST(field1 AS VARCHAR(255))),1,1) AS [Column1]
FROM #tmp
DROP TABLE #tmp
Presumably your question is about parsing 7869. Using the substring function for this:
select substring(cast(<col> as char(4)), 1, 1) as FirstChar,
substring(cast(<col> as char(4)), 2, 1) as SecondChar,
substring(cast(<col> as char(4)), 3, 1) as ThirdChar,
substring(cast(<col> as char(4)), 4, 1) as FourthChar
from YourTable
I might be interpreting the ordering of the digits incorrectly, and this assumes the strings are always 4 digits and that you want characters. An alternative way is just to look at this as numbers:
select <col%10 as FirstNum,
(col/10) %10 as SecondNum,
(col/100)%10 as ThirdNum,
(col/1000)%10 as FourthNum
You can use the modulo operator (%) to get the digits of an integer, without using the slower string manipulation functions, like this
select
value % 100000000 / 10000000,
value % 10000000 / 1000000,
value % 1000000 / 100000,
value % 100000 / 10000,
value % 10000 / 1000,
value % 1000 / 100,
value % 100 / 10,
value % 10
from testData
Here is a SQL Fiddle to play with.
If the field is always 4 numbers long, you can get away with:
select
value / 1000 as Thousands,
value % 1000 / 100 as Hundreds,
value % 100 / 10 as Tens,
value % 10 as Units
from testData
If, however, you need to use it on arbitrary numbers, you could create a user-defined table valued function, that will return the digits in a table, like this:
create function dbo.getDigits(#Input int)
returns #Digits table
(
Digit int,
Position int
)
as begin
declare #pos int
declare #digit int
set #pos = 0
if #input = 0
begin
-- zero is just a single zero digit at position 1
insert into #digits values (0,1)
return
end
while #input<>0 begin
set #pos=#pos+1
set #digit = #input % 10
set #input = #input / 10
insert into #digits values (#digit, #pos)
end
return
end
and use it like this:
SELECT td.ID, td.Value, d.Digit, d.Position
FROM testData td
CROSS APPLY dbo.getDigits(td.Value) AS d
order by td.ID, d.Position Desc
(Here's another SQL Fiddle, based on the previous one)