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

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.

Related

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

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

SQL Server Percent Difference is Greater than Value

I have the following table structures:
table c_alert:
|dynamic|symbol|price_usd|
--------------------------
|5 |BTC |13000 |
table c_current:
|symbol|price_usd|
------------------
|BTC |13600 |
I have this query:
SELECT dbo.c_alert.symbol, dbo.c_alert.price_usd AS alert_price, dbo.c_current.price_usd AS current_price, (dbo.c_current.price_usd - dbo.c_alert.price_usd) * 100.0 / dbo.c_alert.price_usd AS pct_diff, dbo.c_alert.dynamic AS pct
FROM dbo.c_alert INNER JOIN
dbo.c_current
ON dbo.c_alert.symbol = dbo.c_current.symbol AND
dbo.c_alert.dynamic > (dbo.c_current.price_usd - dbo.c_alert.price_usd) * 100.0 / dbo.c_alert.price_usd
Which returns this:
|symbol|alert_price|current_price|pct_diff|dynamic|
-----------------------------------------------
|BTC |13000 |13613.3000 |4.7 |5 |
Not very strong with financial queries...Basically I would like to know when the price difference between alert_price and current_price are equal to or greater than value in the dynamic column as a boolean. So where the difference is equal or greater than 5% show True, else False. That dynamic value (integer) could change for each row in the c_alert table. Hope someone can provide a solution to the query.
Because the same percent difference term is required in multiple places in the query, I might go with using a CTE first, which calculates this term. Then, do a straightforward query on the CTE to get the output you want.
WITH cte AS (
SELECT
t2.symbol,
t2.dynamic,
t2.price_usd AS alert_price,
t1.price_usd AS current_price,
100.0*(t1.price_usd - COALESCE(t2.price_usd, 0.0)) / t2.price_usd AS pct_diff
FROM dbo.c_current t1
LEFT JOIN dbo.c_alert t2
ON t1.symbol = t2.symbol
)
SELECT
symbol,
alert_price,
current_price,
pct_diff,
dynamic,
CASE WHEN pct_diff > dynamic THEN 'TRUE' ELSE 'FALSE' END AS result
FROM cte;
Edit:
The logic seems to be working in the demo below. If you still have issues, then edit the demo and paste the link somewhere as a comment.
Demo
Use table aliases so your query is easier to write and to read. Then just use a case:
SELECT a.symbol, a.price_usd AS alert_price,
c.price_usd AS current_price,
(c.price_usd - a.price_usd) * 100.0 / a.price_usd AS pct_diff,
a.dynamic AS pct,
(case when (a.price_usd - c.price_used) > a.dynamic
then 'true' else 'false'
end) as flag
FROM dbo.c_alert a INNER JOIN
dbo.c_current c
ON a.symbol = c.symbol AND
a.dynamic > (c.price_usd - a.price_usd) * 100.0 / a.price_usd;
SQL Server doesn't have a boolean type, so this uses a string. You can use 0 and 1 instead.

Is there an equivalent of Excel's PERCENTRANK (column,value) in SQL?

Is there an equivalent of Excel's PERCENTRANK (column,value) in SQL? I think native SQL Server functions provide only percentiles from the same column, but am I missing something?
The problem is that I have two columns - A and B - and I need to use distribution of values from column A and then take values from another column and rank them against column A - how do values in B fit in distribution defined by column A (or, precisely: get CDF of column A at point defined by B).
Possible solutions:
Write UDF (they don't parallelize, do they?) - can you answer how could I efficiently structure such UDF?
Use SQL Server 2016 and integrate it with R (R code within SQL code)
Copy data to R, perform calculations, send back to server
Copy data to Excel and calculate it over there "manually", import results back to database
There are multiple columns with values and distributions I need to apply this strategy to, so I am looking for an efficient solution.
Edit - example:
column A column B result
10 16 0,20 =PERCENTRANK($A1:$A4, B1)
20 35 0,83 =PERCENTRANK($A1:$A4, B2)
30 10 0,00 =PERCENTRANK($A1:$A4, B3)
40 25 0,50 =PERCENTRANK($A1:$A4, B4)
This could serve as an approximate, not foolproof numerical solution with column being passed as a name:
CREATE FUNCTION [dbo].[PERCENTRANK_column](
#colname nvarchar(50)
)
RETURNS #resultingtable TABLE(
RowID int,
PercentRank float
)
AS
BEGIN
;WITH true_CDF AS (
SELECT
PERCENT_RANK() OVER (PARTITION BY 1 ORDER BY
CASE #colname
WHEN 'X' THEN X
WHEN 'Y' THEN Y
ELSE NULL
END
ASC) as PercentRank
,
CASE #colname
WHEN 'X' THEN X
WHEN 'Y' THEN Y
ELSE NULL
END AS selected_col
FROM dbo.MainTable
)
, selectColumn AS (
SELECT
m.RowID
,CASE #colname
WHEN 'X' THEN to_score_X
WHEN 'Y' THEN to_score_Y
ELSE NULL
END as to_score
FROM dbo.MainTable m
)
, added_Mins AS (
SELECT
B.RowID
, ABS(B.to_score - A.selected_col) as diff
, MIN(ABS(B.to_score - A.selected_col)) OVER (PARTITION BY 1) as lowest_difference
, A.PercentRank
FROM selectColumn B
CROSS JOIN true_CDF A
)
INSERT INTO #resultingtable
SELECT
RowID, PercentRank
FROM added_Mins
WHERE lowest_difference = diff
RETURN
END

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 Server : convert sub select query to join

I have 2 two tables questionpool and question where question is a many to one of question pool. I have created a query using a sub select query which returns the correct random results but I need to return more than one column from the question table.
The intent of the query is to return a random test from the 'question' table for each 'QuizID' from the 'Question Pool' table.
SELECT QuestionPool.QuestionPoolID,
(
SELECT TOP (1) Question.QuestionPoolID
FROM Question
WHERE Question.GroupID = QuestionPool.QuestionPoolID
ORDER BY NEWID()
)
FROM QuestionPool
WHERE QuestionPool.QuizID = '5'
OUTER APPLY is suited to this:
Select *
FROM QuestionPool
OUTER APPLY
(
SELECT TOP 1 *
FROM Question
WHERE Question.GroupID = QuestionPool.QuestionPoolID
ORDER BY NEWID()
) x
WHERE QuestionPool.QuizID = '5'
Another example of OUTER APPLY use http://www.ienablemuch.com/2012/04/outer-apply-walkthrough.html
Live test: http://www.sqlfiddle.com/#!3/d8afc/1
create table m(i int, o varchar(10));
insert into m values
(1,'alpha'),(2,'beta'),(3,'delta');
create table x(i int, j varchar, k varchar(10));
insert into x values
(1,'a','hello'),
(1,'b','howdy'),
(2,'x','great'),
(2,'y','super'),
(3,'i','uber'),
(3,'j','neat'),
(3,'a','nice');
select m.*, '' as sep, r.*
from m
outer apply
(
select top 1 *
from x
where i = m.i
order by newid()
) r
Not familiar with SQL server, but I hope this would do:
Select QuestionPool.QuestionPoolID, v.QuestionPoolID, v.xxx -- etc
FROM QuestionPool
JOIN
(
SELECT TOP (1) *
FROM Question
WHERE Question.GroupID = QuestionPool.QuestionPoolID
ORDER BY NEWID()
) AS v ON v.QuestionPoolID = QuestionPool.QuestionPoolID
WHERE QuestionPool.QuizID = '5'
Your query appears to be bringing back an arbitrary Question.QuestionPoolId for each QuestionPool.QuestionPoolId subject to the QuizId filter.
I think the following query does this:
select qp.QuestionPoolId, max(q.QuestionPoolId) as any_QuestionPoolId
from Question q join
qp.QuestionPoolId qp
on q.GroupId = qp.QuestionPoolId
WHERE QuestionPool.QuizID = '5'
group by qp.QuestionPoolId
This returns a particular question.
The following query would allow you to get more fields:
select qp.QuestionPoolId, q.*
from (select q.*, row_number() over (partition by GroupId order by (select NULL)) as randrownum
from Question q
) join
(select qp.QuestionPoolId, max(QuetionPool qp
on q.GroupId = qp.QuestionPoolId
WHERE QuestionPool.QuizID = '5' and
randrownum = 1
This uses the row_number() to arbitrarily enumerate the rows. The "Select NULL" provides the random ordering (alternatively, you could use "order by GroupId".
Common Table Expressions (CTEs) are rather handy for this type of thing...
http://msdn.microsoft.com/en-us/library/ms175972(v=sql.90).aspx