can you set environmental variable to round numbers in SQL Server query? - sql

I have a bunch of calculations in a SQL Server 2012 query, kind of like:
select T1_month
,a.some_value, b.value_to_compare,(select (some_value - value_to_compare)/value_to_compare*100 where value_to_compare != 0) percent_diff
from
(select T1_month
,sum(some_value) some_value
from T1
group by T1_month) a
join
(select T2_month
,sum(value_to_compare) value_to_compare
from T2
group by T2_month) b
on a.T1_month = b.T2_month
order by T1_month;
I used a round function here, but I need to add a lot more similar lines. Is there any way to just set a global variable to round all columns in one shot? Otherwise it's just a lot of leg work.
round((some_value - value_to_compare)/value_to_compare*100, 2)
I'll be pasting to Excel but it would be nice to round it in the source without having to use the round function so many times.

Here is a workaround, no need to type Round() function for every line:
SELECT 9.0 / 7 * 100 AS Direct_Query
CREATE TABLE #Table_1 (
[Column_1] [NUMERIC](18, 2) NULL
)
INSERT INTO #Table_1
SELECT 9.0/7 * 100
SELECT Column_1 AS Temp_Table_Formatted FROM #Table_1
Edit:
If you cannot use temp table, you can wrap your query with CTE then just Round() the result of CTE, which is pretty easy to do with help of a multiline editor like Sublime Text or VisualStudio Code:
WITH CTE_Result_To_Format
AS (
SELECT
T1_month
,a.some_value
,b.value_to_compare
,(
SELECT
(some_value - value_to_compare) / value_to_compare * 100
WHERE value_to_compare != 0
)
percent_diff
FROM (
SELECT
T1_month
,SUM(some_value) some_value
FROM T1
GROUP BY
T1_month
) a
JOIN (
SELECT
T2_month
,SUM(value_to_compare) value_to_compare
FROM T2
GROUP BY
T2_month
) b
ON a.T1_month = b.T2_month
)
SELECT
r.T1_month
,ROUND(r.some_value, 2) AS some_value
,ROUND(r.value_to_compare, 2) AS value_to_compare
,ROUND(r.percent_diff, 2) AS percent_diff
FROM CTE_Result_To_Format r
ORDER BY
r.T1_month

Related

How to call a calculated column in "from" in sql server

I'm trying to call this calculated column 'RelativeEffectiveSpreadAbsoluteValue' in SQL servers' FROM part.
, case when cast(sa.Mid_Price as float) = 0
then 0
else ((CAST(sa.Ask_Price as float)-cast(sa.Bid_Price as float))/CAST(sa.Mid_Price as float))/(0.01/100)
end As RelativeEffectiveSpreadAbsoluteValue
like this, but the SQL server won't recognize it
left join [RISK].[dbo].[FILiquidityBuckets] FB6
ON FB6.Metric = 'Relative spread ' AND (
((CAST(RelativeEffectiveSpreadAbsoluteValue AS FLOAT)>= 0 AND CAST(RelativeEffectiveSpreadAbsoluteValue AS FLOAT)< 1000000) AND
FB6.LiquidityScore = 5) OR
((CAST(RelativeEffectiveSpreadAbsoluteValue AS FLOAT)>= 1000000 AND CAST(RelativeEffectiveSpreadAbsoluteValue AS FLOAT)<10000000) AND
FB6.LiquidityScore = 4) OR
((CAST(RelativeEffectiveSpreadAbsoluteValue AS FLOAT)>= 10000000 AND CAST(RelativeEffectiveSpreadAbsoluteValue AS FLOAT)< 100000000) AND
FB6.LiquidityScore = 3) OR
((CAST(RelativeEffectiveSpreadAbsoluteValue AS FLOAT)>= 100000000 AND CAST(RelativeEffectiveSpreadAbsoluteValue AS FLOAT)<1000000000) AND
FB6.LiquidityScore = 2) OR
(CAST(RelativeEffectiveSpreadAbsoluteValue AS FLOAT) >= 1000000000 AND F65.LiquidityScore = 1)
)
So far I know by using 'Cross Apply' a calculated column can calculate another column in the same view,
like this example
Select
ColumnA,
ColumnB,
c.calccolumn1 As calccolumn1,
c.calccolumn1 / ColumnC As calccolumn2
from t42
cross apply (select (ColumnA + ColumnB) as calccolumn1) as c
but this is only for the select part, can we use it in the From part?
Please help thank you!
Put the apply operation which does the calculation prior to the join in your query:
create table t(a int);
create table u(b int);
select t.a,
t2.calculatedColumn,
u.b
from t
cross apply (select t.a * 2) as t2 (calculatedColumn)
left join u on u.b = t2.calculatedColumn
As Panagiotis observed, this may result in a slow join because the join predicate will not be able to use an index. But if the nature of your query demands it, the language supports it.
If you need this to be fast, create an indexed computed column on the table you have aliased as sa instead of calculating it in the query. Since your column will be of type float, you will need to mark the computed column as persisted before you can index it.

can't use JOIN with generate_series on Redshift

generate_series function on Redshift works as expected, when used in a simple select statement.
WITH series AS (
SELECT n as id from generate_series (-10, 0, 1) n
) SELECT * FROM series;
-- Works fine
As soon as I add a JOIN condition, redshift throws
com.amazon.support.exceptions.ErrorException: Function
generate_series(integer,integer,integer)" not supported"
DROP TABLE testing;
CREATE TABLE testing (
id INT
);
WITH series AS (
SELECT n as id from generate_series (-10, 0, 1) n
) SELECT * FROM series S JOIN testing T ON S.id = T.id;
-- Function "generate_series(integer,integer,integer)" not supported.
Redshift Version
SELECT version();
-- PostgreSQL 8.0.2 on i686-pc-linux-gnu, compiled by GCC gcc (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3), Redshift 1.0.1485
Are there any workarounds to make this work?
generate_series is not supported by Redshift. It works only standalone on a leader node.
A workaround would be using row_number against any table that has sufficient number of rows:
with
series as (
select (row_number() over ())-11 from some_table limit 10
) ...
also, this question was asked multiple times already
You are correct that this does not work on Redshift.
See here.
The easiest workaround is to create a permanent table "manually" beforehand with the values within that table, e.g. you could have rows on that table for -1000 to +1000, then select the range from that table,
So for your example you would have something like
WITH series AS (
SELECT n as id from (select num as n from newtable where num between -10 and 0) n
) SELECT * FROM series S JOIN testing T ON S.id = T.id;
Does that work for you?
Alternatively, if you cannot create the table beforehand or prefer not to, you could use something like this
with ten_numbers as (select 1 as num union select 2 union select 3 union select 4 union select 5 union select 6 union select 7 union select 8 union select 9 union select 0)
,generted_numbers AS
(
SELECT (1000*t1.num) + (100*t2.num) + (10*t3.num) + t4.num-5000 as gen_num
FROM ten_numbers AS t1
JOIN ten_numbers AS t2 ON 1 = 1
JOIN ten_numbers AS t3 ON 1 = 1
JOIN ten_numbers AS t4 ON 1 = 1
)
select gen_num from generted_numbers
where gen_num between -10 and 0
order by 1;

Get every combination of sort order and value of a csv

If I have a string with numbers separated by commas, like this:
Declare #string varchar(20) = '123,456,789'
And would like to return every possible combination + sort order of the values by doing this:
Select Combination FROM dbo.GetAllCombinations(#string)
Which would in result return this:
123
456
789
123,456
456,123
123,789
789,123
456,789
789,456
123,456,789
123,789,456
456,789,123
456,123,789
789,456,123
789,123,456
As you can see not only is every combination returned, but also each combination+sort order as well. The example shows only 3 values separated by commas, but should parse any amount--Recursive.
The logic needed would be somewhere in the realm of using a WITH CUBE statement, but the problem with using WITH CUBE (in a table structure instead of CSV of course), is that it won't shuffle the order of the values 123,456 456,123 etc., and will only provide each combination, which is only half of the battle.
Currently I have no idea what to try. If someone can provide some assistance it would be appreciated.
I use a User Defined Table-valued Function called split_delimiter that takes 2 values: the #delimited_string and the #delimiter_type.
CREATE FUNCTION [dbo].[split_delimiter](#delimited_string VARCHAR(8000), #delimiter_type CHAR(1))
RETURNS TABLE AS
RETURN
WITH cte10(num) 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
)
,cte100(num) AS
(
SELECT 1
FROM cte10 t1, cte10 t2
)
,cte10000(num) AS
(
SELECT 1
FROM cte100 t1, cte100 t2
)
,cte1(num) AS
(
SELECT TOP (ISNULL(DATALENGTH(#delimited_string),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM cte10000
)
,cte2(num) AS
(
SELECT 1
UNION ALL
SELECT t.num+1
FROM cte1 t
WHERE SUBSTRING(#delimited_string,t.num,1) = #delimiter_type
)
,cte3(num,[len]) AS
(
SELECT t.num
,ISNULL(NULLIF(CHARINDEX(#delimiter_type,#delimited_string,t.num),0)-t.num,8000)
FROM cte2 t
)
SELECT delimited_item_num = ROW_NUMBER() OVER(ORDER BY t.num)
,delimited_value = SUBSTRING(#delimited_string, t.num, t.[len])
FROM cte3 t;
Using that I was able to parse the CSV to a table and join it back to itself multiple times and use WITH ROLLUP to get the permutations you are looking for.
WITH Numbers as
(
SELECT delimited_value
FROM dbo.split_delimiter('123,456,789',',')
)
SELECT CAST(Nums1.delimited_value AS VARCHAR)
,ISNULL(CAST(Nums2.delimited_value AS VARCHAR),'')
,ISNULL(CAST(Nums3.delimited_value AS VARCHAR),'')
,CAST(Nums4.delimited_value AS VARCHAR)
FROM Numbers as Nums1
LEFT JOIN Numbers as Nums2
ON Nums2.delimited_value not in (Nums1.delimited_value)
LEFT JOIN Numbers as Nums3
ON Nums3.delimited_value not in (Nums1.delimited_value, Nums2.delimited_value)
LEFT JOIN Numbers as Nums4
ON Nums4.delimited_value not in (Nums1.delimited_value, Nums2.delimited_value, Nums3.delimited_value)
GROUP BY CAST(Nums1.delimited_value AS VARCHAR)
,ISNULL(CAST(Nums2.delimited_value AS VARCHAR),'')
,ISNULL(CAST(Nums3.delimited_value AS VARCHAR),'')
,CAST(Nums4.delimited_value AS VARCHAR) WITH ROLLUP
If you will potentially have more than 3 or 4, you'll want to expand your code accordingly.

Reuse subquery result in WHERE-Clause for INSERT

i am using Microsoft SQL Server 2008
i would like to save the result of a subquery to reuse it in a following subquery.
Is this possible?
What is best practice to do this? (I am very new to SQL)
My query looks like:
INSERT INTO [dbo].[TestTable]
(
[a]
,[b]
)
SELECT
(
SELECT TOP 1 MAT_WS_ID
FROM #TempTableX AS X_ALIAS
WHERE OUTERBASETABLE.LT_ALL_MATERIAL = X_ALIAS.MAT_RM_NAME
)
,(
SELECT TOP 1 MAT_WS_NAME
FROM #TempTableY AS Y_ALIAS
WHERE Y_ALIAS.MAT_WS_ID = MAT_WS_ID
--(
--SELECT TOP 1 MAT_WS_ID
--FROM #TempTableX AS X_ALIAS
--WHERE OUTERBASETABLE.LT_ALL_MATERIAL = X_ALIAS.MAT_RM_NAME
--)
)
FROM [dbo].[LASERTECHNO] AS OUTERBASETABLE
My question is:
Is this correct what i did.
I replaced the second SELECT Statement in the WHERE-Clause for [b] (which is commented out and exactly the same as for [a]), with the result of the first SELECT Statement of [a] (=MAT_WS_ID).
It seems to give the right results.
But i dont understand why!
I mean MAT_WS_ID is part of both temporary tables X_ALIAS and Y_ALIAS.
So in the SELECT statement for [b], in the scope of the [b]-select-query, MAT_WS_ID could only be known from the Y_ALIAS table. (Or am i wrong, i am more a C++, maybe the scope things in SQL and C++ are totally different)
I just wannt to know what is the best way in SQL Server to reuse an scalar select result.
Or should i just dont care and copy the select for every column and the sql server optimizes it by its own?
One approach would be outer apply:
SELECT mat.MAT_WS_ID
, (
SELECT TOP 1 MAT_WS_NAME
FROM #TempTableY AS Y_ALIAS
WHERE Y_ALIAS.MAT_WS_ID = mat.MAT_WS_ID
)
FROM [dbo].[LASERTECHNO] AS OUTERBASETABLE
OUTER APPLY
(
SELECT TOP 1 MAT_WS_ID
FROM #TempTableX AS X_ALIAS
WHERE OUTERBASETABLE.LT_ALL_MATERIAL = X_ALIAS.MAT_RM_NAME
) as mat
You could rank rows in #TempTableX and #TempTableY partitioning them by MAT_RM_NAME in the former and by MAT_WS_ID in the latter, then use normal joins with filtering by rownum = 1 in both tables (rownum being the column containing the ranking numbers in each of the two tables):
WITH x_ranked AS (
SELECT
*,
rownum = ROW_NUMBER() OVER (PARTITION BY MAT_RM_NAME ORDER BY (SELECT 1))
FROM #TempTableX
),
y_ranked AS (
SELECT
*,
rownum = ROW_NUMBER() OVER (PARTITION BY MAT_WS_ID ORDER BY (SELECT 1))
FROM #TempTableY
)
INSERT INTO dbo.TestTable (a, b)
SELECT
x.MAT_WS_ID,
y.MAT_WS_NAME
FROM dbo.LASERTECHNO t
LEFT JOIN x_ranked x ON t.LT_ALL_MATERIAL = x.MAT_RM_NAME AND x.rownum = 1
LEFT JOIN y_ranked y ON x.MAT_WS_ID = y.MAT_WS_ID AND y.rownum = 1
;
The ORDER BY (SELECT 1) bit is a trick to specify an indeterminate ordering, which, accordingly, would result in indeterminate rownum = 1 rows picked by the query. That is to more or less duplicate your TOP 1 without an explicit order, but I would recommend you to specify a more sensible ORDER BY clause to make the results more predictable.

sql zero leading in comparing 2 record set

I have a problem compare 2 set of data with and without leading zero.
In the Inventory_NBP, there is as 000000000000909120.
However, in PDM_Analysis, there exist only 909120 without the leading zero.
The current query below failed to retrieve any 000000000000909120 or 909120 as the "IN" condition is not met.
How do i modify the query below to fulfill my requirement?
sel * FROM Inventory_NBP.v_dmnd_rsrv_dpnd_rqr_mrp
WHERE plnt_id ='WA01'
and mtrl_id in('G29329-001', '000000000000909120', '13-0006-001')
and
(mtrl_id, plnt_id)
IN
( SELECT itm_cd, sap_plnt_cd
FROM PDM_Analysis.v_itm_plnt_extn
)
Do casting. This may solve your problem. Please check.
SELECT * FROM Inventory_NBP.v_dmnd_rsrv_dpnd_rqr_mrp
WHERE plnt_id ='WA01'
and mtrl_id in('G29329-001',CAST(CAST('000000000000909120' AS INT) AS VARCHAR(10)), '13-0006-001')
and
(mtrl_id, plnt_id)
IN
( SELECT itm_cd, sap_plnt_cd
FROM PDM_Analysis.v_itm_plnt_extn
)
Here's an example of how to match values regardless of leading zeros by padding (SQL Server 2008 syntax):
WITH T1
AS
(
SELECT *
FROM (
VALUES ('000000000000909120'),
('00000000099'),
('000000055'),
('22'),
('152')
) AS T (data_col)
),
T2
AS
(
SELECT *
FROM (
VALUES ('909120'),
('99'),
('00055'),
('0000000022'),
('152')
) AS T (data_col)
)
SELECT *
FROM T1 INNER JOIN T2
ON T1.data_col
= REPLICATE('0', LEN(T1.data_col) - LEN(T2.data_col))
+ T2.data_col
UNION
SELECT *
FROM T1 INNER JOIN T2
ON T2.data_col
= REPLICATE('0', LEN(T2.data_col) - LEN(T1.data_col))
+ T1.data_col;