Help with RANK() Over Scalar Function - SQL Server 2008 - tsql

I have the following Inline Table-Valued Function:
SELECT Locations.LocationId,
dbo.Search_GetSuitability(#SearchPreferences,
Score.FieldA, Score.FieldB, Score.FieldC) AS OverallSuitabilityScore,
RANK() OVER (ORDER BY OverallSuitabilityScore) AS OverallSuitabilityRank
FROM dbo.LocationsView Locations
INNER JOIN dbo.LocationScores Score ON Locations.LocationId = Score.LocationId
WHERE Locations.CityId = #LocationId
That RANK() line is giving me an error:
Invalid column name 'OverallSuitabilityScore'.
The function dbo.Search_GetSuitability is a scalar-function which returns a DECIMAL(8,5). I need to assign a rank to each row based on that value.
The only way i can get the above to work is to add the scalar function call in the ORDER BY part again - which is silly. I have about 5 of these scalar function calls and i need seperate RANK() values for each.
What can i do? Can i use a Common Table Expression (CTE) ?

Yep, you can't reference a column alias in the SELECT clause. The CTE sounds good though. Here's an example
WITH Score as
(
select Score.LocationId, Score.FieldA, Score.FieldB, Score.FieldC,
dbo.Search_GetSuitability(#SearchPreferences,
Score.FieldA, Score.FieldB, Score.FieldC) AS OverallSuitabilityScore
from dbo.LocationScores
)
SELECT TOP(10)
Locations.LocationId,
Score.OverallSuitabilityScore,
RANK() OVER (ORDER BY OverallSuitabilityScore) AS OverallSuitabilityRank
FROM dbo.LocationsView Locations
INNER JOIN Score ON Locations.LocationId = Score.LocationId
WHERE Locations.CityId = #LocationId

An old school way of doing this is just to SUBQUERY the expression.
The CTE here only moves the subquery to the top
SELECT TOP(10) LocationId,
OverallSuitabilityScore,
RANK() OVER (ORDER BY OverallSuitabilityScore) AS OverallSuitabilityRank
FROM
(
SELECT
Locations.LocationId,
dbo.Search_GetSuitability(#SearchPreferences,
Score.FieldA, Score.FieldB, Score.FieldC) AS OverallSuitabilityScore
FROM dbo.LocationsView Locations
INNER JOIN dbo.LocationScores Score ON Locations.LocationId = Score.LocationId
WHERE Locations.CityId = #LocationId
) X

Related

How to use new created column in where column in sql?

Hi I have a query which looks like the following :
SELECT device_id, tag_id, at, _deleted, data,
row_number() OVER (PARTITION BY device_id ORDER BY at DESC) AS row_num
FROM mdb_history.devices_tags_mapping_history
WHERE at <= '2019-04-01'
AND _deleted = False
AND (tag_id = '275674' or tag_id = '275673')
AND row_num = 1
However when I run the following query, I get the following error :
ERROR: column "row_num" does not exist
Is there any way to go about this. One way I tried was to use it in the following way:
SELECT * from (SELECT device_id, tag_id, at, _deleted, data,
row_number() OVER (PARTITION BY device_id ORDER BY at DESC) AS row_num
FROM mdb_history.devices_tags_mapping_history
WHERE at <= '2019-04-01'
AND _deleted = False
AND (tag_id = '275674' or tag_id = '275673')) tag_deleted
WHERE tag_deleted.row_num = 1
But this becomes way too complicated as I do it with other queries as I have number of join and I have to select the column as stated from so it causes alot of select statement. Any smart way of doing that in a more simpler way. Thanks
You can't refer to the row_num alias which you defined in the same level of the select in your query. So, your main option here would be to subquery, where row_num would be available. But, Postgres actually has an option to get what you want in another way. You could use DISTINCT ON here:
SELECT DISTINCT ON (device_id), device_id, tag_id, at, _deleted, data
FROM mdb_history.devices_tags_mapping_history
WHERE
at <= '2019-04-01' AND
_deleted = false AND
tag_id IN ('275674', '275673')
ORDER BY
device_id,
at DESC;
Too long/ formatted for a comment. There is a reason behind #TimBiegeleisen statement "alias which you defined in the same level of the select". That reason is that all SQL statement follow the same sequence for evaluation. Unfortunately that sequence does NOT follow the sequence of clauses within the statement presentation. that sequence is in order:
from
where
group by
having
select
limits
You will notice that what actually gets selected fall well after evaluation of the where clause. Since your alias is defined within the select phase it does not exist during the where phase.

why am I getting ERROR: syntax error at end of input?

I am keep getting 'syntax error at end of input' and don't know why.
What I want to do is divide result of disease by result of total with showing condition_id in disease section.
select disease.condition_id, (disease::float/total::float) as prevalence
from (
select condition_id, count(person_id)
from a.condition
where condition_id=316139
group by condition_id
) as disease
join (
select count(distinct person_id) as total
from a.person
)as total;
Can someone please help me with this?
Thanks!
I don't have an exact fix for your current syntax, but I would phrase this query as a join with an aggregation over the entire tables:
SELECT
COUNT(*) FILTER (WHERE c.condition_id = 316139) /
COUNT(DISTINCT p.person_id) AS prevalence
FROM a.person p
LEFT JOIN a.condition c
ON p.person_id = c.person_id;
The main reason for your error is the missing join condition. The join operator requires a join condition (defined using ON).
But given the structure of your query I think you don't actually want a inner join, but a cross join between the two.
Additionally the expression disease::float is trying to cast a complete row to a float value, not a single column. I assume you wanted to alias the count aggregate to something, e.g. count(person_id) as num_persons
Using total::float is also ambiguous as you have a sub-query alias with that name and a column with that name. That is highly confusing, you should avoid that.
select disease.condition_id,
(disease.num_person::float / total.total::float) as prevalence
from (
select condition_id, count(person_id) as num_person
from a.condition
where condition_id = 316139
group by condition_id
) as disease
cross join (
select count(distinct person_id) as total
from a.person
) as total

How to translate SQL to DAX, Need to add FILTER

I want to create calculated table that will summarize In_Force Premium from existing table fact_Premium.
How can I filter the result by saying:
TODAY() has to be between `fact_Premium[EffectiveDate]` and (SELECT TOP 1 fact_Premium[ExpirationDate] ORDE BY QuoteID DESC)
In SQL I'd do that like this:
`WHERE CONVERT(date, getdate()) between CONVERT(date, tblQuotes.EffectiveDate)
and (
select top 1 q2.ExpirationDate
from Table2 Q2
where q2.ControlNo = Table1.controlno
order by quoteid` desc
)
Here is my DAX statement so far:
In_Force Premium =
FILTER(
ADDCOLUMNS(
SUMMARIZE(
//Grouping necessary columns
fact_Premium,
fact_Premium[QuoteID],
fact_Premium[Division],
fact_Premium[Office],
dim_Company[CompanyGUID],
fact_Premium[LineGUID],
fact_Premium[ProducerGUID],
fact_Premium[StateID],
fact_Premium[ExpirationDate]
),
"Premium", CALCULATE(
SUM(fact_Premium[Premium])
),
"ControlNo", CALCULATE(
DISTINCTCOUNT(fact_Premium[ControlNo])
)
), // Here I need to make sure TODAY() falls between fact_Premium[EffectiveDate] and (SELECT TOP 1 fact_Premium[ExpirationDate] ORDE BY QuoteID DESC)
)
Also, what would be more efficient way, to create calculated table from fact_Premium or create same table using sql statement (--> Get Data--> SQL Server) ?
There are 2 potential ways in T-SQL to get the next effective date. One is to use LEAD() and another is to use an APPLY operator. As there are few facts to work with here are samples:
select *
from (
select *
, lead(EffectiveDate) over(partition by CompanyGUID order by quoteid desc) as NextEffectiveDate
from Table1
join Table2 on ...
) d
or
select table1.*, oa.NextEffectiveDate
from Table1
outer apply (
select top(1) q2.ExpirationDate AS NextEffectiveDate
from Table2 Q2
where q2.ControlNo = Table1.controlno
order by quoteid desc
) oa
nb. an outer apply is a little similar to a left join in that it will allow rows with a NULL to be returned by the query, if that is not needed than use cross apply instead.
In both these approaches you may refer to NextEffectiveDate in a final where clause, but I would prefer to avoid using the convert function if that is feasible (this depends on the data).

Implement ROW_NUMBER() in beamSQL

I have the below query :
SELECT DISTINCT Summed, ROW_NUMBER () OVER (order by Summed desc) as Rank from table1
I have to write it in Apache Beam(beamSql). Below is my code :
PCollection<BeamRecord> rec_2_part2 = rec_2.apply(BeamSql.query("SELECT DISTINCT Summed, ROW_NUMBER(Summed) OVER (ORDER BY Summed) Rank1 from PCOLLECTION "));
But I'm getting the below error :
Caused by: java.lang.UnsupportedOperationException: Operator: ROW_NUMBER is not supported yet!
Any idea how to implement ROW_NUMBER() in beamSql ?
Here is one way you can approximate your current query without using ROW_NUMBER:
SELECT
t1.Summed,
(SELECT COUNT(*) FROM (SELECT DISTINCT Summed FROM table1) t2
WHERE t2.Summed >= t1.Summed) AS Rank
FROM
(
SELECT DISTINCT Summed
FROM table1
) t1
The basic idea is to first subquery to get a table with only distinct Summed values. Then, use a correlated subquery to simulate the row number. This isn't a very efficient method, but if ROW_NUMBER is not available, then you're stuck with some alternative.
The solution which worked for the above query:
PCollection<BeamRecord> rec_2 = rec_1.apply(BeamSql.query("SELECT max(Summed) as maxed, max(Summed)-10 as least, 'a' as Dummy from PCOLLECTION"));

In Firebird, how to aggregate the first N rows?

I would like to do something like this:
CNT=2;
//[edit]
select avg(price) from (
select first :CNT p.Price
from Price p
order by p.Date desc
);
This does not work, Firebird does not allow :cnt as a parameter to FIRST. I need to average the first CNT newest prices. The number 2 changes so it can not be hard-coded.
This can be broken out into a FOR SELECT loop and break when a count is reached. Is that the best way though? Can this be done in a single SQL statement?
Creating the SQL as a string and running it is not the best fit either. It is important that the database compile my SQL statement.
You don't have to use CTE, you can do it directly:
select avg(price) from (
select first :cnt p.Price
from Price p
order by p.Date desc
);
You can use a CTE (Common Table Expression) (see http://www.firebirdsql.org/refdocs/langrefupd21-select.html#langrefupd21-select-cte) to select data before calculate average.
See example below:
with query1 as (
select first 2 p.Price
from Price p
order by p.Date desc
)
select avg(price) from query1