'morning!
I'm noticing a weird behaviour of aliases in queries: I executed this query (notice the use of alias in the WHERE clause)
SELECT traversedElement(1).start AS stime, traversedElement(2) AS destination
FROM (TRAVERSE both('Involves') FROM #11:11052 WHILE $depth <= 2)
WHERE #rid = #11:8395
AND stime = DATE('31.10.2014 11:00:00')
and I obtained an empty result, while avoiding the use of the stime alias in the WHERE clause causes the query to produce the right result.
SELECT traversedElement(1).start AS stime, traversedElement(2) AS destination
FROM (TRAVERSE both('Involves') FROM #11:11052 WHILE $depth <= 2)
WHERE #rid = #11:8395
AND traversedElement(1).start = DATE('31.10.2014 11:00:00')
Does anybody know what's going on? Are aliases not processed in WHERE clauses?
#Alberto
If you use an alias you can't use it immediately in where clause.
You have two different ways to execute your query
1)
select from (SELECT traversedElement(1).start AS stime, traversedElement(2) AS destination
FROM (TRAVERSE both('Involves') FROM #11:11052 WHILE $depth <= 2)
WHERE #rid = #11:8395) where stime = DATE('31.10.2014 11:00:00')
2)
select start as stime,destination from (SELECT traversedElement(1).start, traversedElement(2) AS destination
FROM (TRAVERSE both('Involves') FROM #11:11052 WHILE $depth <= 2)
WHERE #rid = #11:8395 AND traversedElement(1).start = DATE('31.10.2014 11:00:00'))
Related
Can I modify the next to use the column aliases avg_time and cnt in an expression ROUND(avg_time * cnt, 2)?
SELECT
COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time,
MAX(time) as max_time,
ROUND(AVG(time), 2) as avg_time,
MIN(time) as min_time,
COUNT(path) as cnt,
ROUND(avg_time * cnt, 2) as slowdown, path
FROM
loadtime
GROUP BY
path
ORDER BY
avg_time DESC
LIMIT 10;
It raises the next error:
ERROR: column "avg_time" does not exist
LINE 7: ROUND(avg_time * cnt, 2) as slowdown, path
The next, however, works fine (use primary expressions instead of column aliases:
SELECT
COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time,
MAX(time) as max_time,
ROUND(AVG(time), 2) as avg_time,
MIN(time) as min_time,
COUNT(path) as cnt,
ROUND(AVG(time) * COUNT(path), 2) as slowdown, path
FROM
loadtime
GROUP BY
path
ORDER BY
avg_time DESC
LIMIT 10;
You can use a previously created alias in the GROUP BY or HAVING statement but not in a SELECT or WHERE statement. This is because the program processes all of the SELECT statement at the same time and doesn't know the alias' value yet.
The solution is to encapsulate the query in a subquery and then the alias is available outside.
SELECT stddev_time, max_time, avg_time, min_time, cnt,
ROUND(avg_time * cnt, 2) as slowdown
FROM (
SELECT
COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time,
MAX(time) as max_time,
ROUND(AVG(time), 2) as avg_time,
MIN(time) as min_time,
COUNT(path) as cnt,
path
FROM
loadtime
GROUP BY
path
ORDER BY
avg_time DESC
LIMIT 10
) X;
The order of execution of a query (and thus the evaluation of expressions and aliases) is NOT the same as the way it is written. The "general" position is that the clauses are evaluated in this sequence:
FROM
WHERE
GROUP BY
HAVING
SELECT
ORDER BY
Hence the column aliases are unknown to most of the query until the select clause is complete (and this is why you can use aliases in the ORDER BY clause). However table aliases which are established in the from clause are understood in the where to order by clauses.
The most common workaround is to encapsulate your query into a "derived table"
Suggested reading: Order Of Execution of the SQL query
Note: different SQL dbms have different specific rules regarding use of aliases
EDIT
The purpose behind reminding readers of the logical clause sequence is that often (but not always) aliases only becomes referable AFTER the clause where the alias is declared. The most common of which is that aliases declared in the SELECT clause can be used by the ORDER BY clause. In particular, an alias declared in a SELECT clause cannot be referenced within the same SELECT clause.
But please do note that due to differences in products not every dbms will behave in this manner
Aliases are not available until the virtual relation is actually created, if you want to do additional expressions using the aliases themselves you will have to create the virtual relation using as sub-query than run an additional query on top of it. So I would modify your query to the following:
SELECT stddev_time, max_time, avg_time, min_time, ROUND(avg_time * cnt, 2) as slowdown, path FROM
(
SELECT
COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time,
MAX(time) as max_time,
ROUND(AVG(time), 2) as avg_time,
MIN(time) as min_time,
COUNT(path) as cnt,
ROUND(AVG(time) * COUNT(path), 2) as slowdown, path
FROM
loadtime
GROUP BY
path
ORDER BY
avg_time DESC
LIMIT 10;
)
I want to add here the reason your second query worked is because the query planner recognized those columns as defined directly in the table you're querying them from.
Either repeat the expressions:
ROUND(ROUND(AVG(time), 2) * COUNT(path), 2) as slowdown
or use an subquery:
SELECT *, ROUND(avg_time * cnt, 2) as slowdown FROM (
SELECT
COALESCE(ROUND(stddev_samp(time), 2), 0) as stddev_time,
MAX(time) as max_time,
ROUND(AVG(time), 2) as avg_time,
MIN(time) as min_time,
COUNT(path) as cnt,
path
FROM loadtime
GROUP BY path) x
ORDER BY avg_time DESC
LIMIT 10;
I have a T-SQL query that return X records ordered.
I want to get only on record , for instance, only the 5th record from that result: how ?
Thanks
For that you have to update your query.
I.e in oracle we have rownum that assign rownumber to every row.
You can do like this,
Select * from(
Select a.*, rownum as n from your table) where n = 3;
Something like this.
Try this:
WITH NumberedTable AS
(
SELECT
RowNo = ROW_NUMBER() OVER(ORDER BY <'Order Column here'>)
, *
FROM <'Table Name here'>
)
SELECT *
FROM NumberedTable
WHERE RowNo = <'Record No. here'>
I have 3 tables:
with current_exclusive as(
select id_station, area_type,
count(*) as total_entries
from c1169.data_cashier
where id_station IN(2439,2441,2443,2445,2447,2449) and date >= '2017-10-30' and date <= '2017-12-30'
group by id_station, area_type
), current_table as(
select id_station, area_type,
sum(total_time) filter (where previous_status = 1) as total_time
from c1169.data_table
where id_station IN(2439,2441,2443,2445,2447,2449) and date >= '2017-10-30' and date < '2017-12-30'
group by id_station, area_type
), current_cashier as(
select id_station, area_type,
sum(1) as total_transactions
from c1169.data_cashier
where id_station IN(2439,2441,2443,2445,2447,2449) and date >= '2017-10-30' and date < '2017-12-30'
group by id_station, area_type
)
select *
from current_exclusive
full join current_table on current_exclusive.id_station = current_table.id_station and current_exclusive.area_type = current_table.area_type
full join current_cashier on current_exclusive.id_station = current_cashier.id_station and current_exclusive.area_type = current_cashier.area_type
and the result is:
but my expected result is:
Are there any way to select * and show the expected result? Because when I do full join then id_station and area_type can be null in some tables, so it very hard to choose which column is not null.
Like: select case id_station is not null then id_station else id_station1 end, but I have up to 10 tables so can not do in select case
Use USING, per the documentation:
USING ( join_column [, ...] )
A clause of the form USING ( a, b, ... ) is shorthand for ON left_table.a = right_table.a AND left_table.b = right_table.b .... Also, USING implies that only one of each pair of equivalent columns will be included in the join output, not both.
select *
from current_exclusive
full join current_table using (id_station, area_type)
full join current_cashier using (id_station, area_type)
You cannot accomplish anything if you insist on using select *, since you are getting the values from different tables.
The option you have is to include a COALESCE block which gives you the first non-null value from the list of columns.
So, you could use.
select COALESCE( current_exclusive.id_station, current_table.id_station, current_cashier.id_station ) as id_station ,
COALESCE( current_exclusive.area_type , current_table.area_type, current_cashier.area_type ) as area_type ,.....
...
from current_exclusive
full join current_table..
...
I have somewhat complex SQL query that I need to convert to nice and clean AR code, and I'm having some troubles with it.
Here's the query:
SELECT a.*
FROM
fixed_assets a
INNER JOIN
(
SELECT e.id, e.fixed_asset_id
FROM
fixed_asset_book_entries e
WHERE e.book_id = %SOME_VALUE_1%
) e_mod
ON e_mod.fixed_asset_id = a.id
INNER JOIN
(
SELECT s.fixed_asset_book_entry_id,
s.status,
ROW_NUMBER() OVER (PARTITION BY s.fixed_asset_book_entry_id ORDER BY s.created_at DESC) AS rn
FROM
status_changes s
WHERE s.created_at < %SOME_VALUE_2%
) s_mod
ON s_mod.fixed_asset_book_entry_id = e_mod.id AND s_mod.rn = 1 AND s_mod.status <> 'inactive'
ORDER BY a.id;
So, the point of it all is to extract such fixed_assets rows, that have related fixed_asset_book_entries with certain book_id, and it's last status_change before certain date has any status except inactive.
What I want to end up with is a class-level (scope?) method FixedAsset.active_within_book_on_date(book_id, date), that will return FixedAsset objects, that comply with restrictions I've explained above. I'm familiar with joins method, but I'm not sure how to handle row_number function except passing raw SQL to joins call.
I think the best you can do is something like the following. In lib/sql_template.rb:
class SqlTemplate
attr_reader :sql
# Load the file and process the ERB
# Call it like this:
# sql = SqlTemplate.new(filename, binding)
def initialize(filename, the_binding)
raw_code = File.read(File.join(Rails.root, 'lib/sql', filename))
template = ERB.new(raw_code)
#sql = template.result(the_binding)
end
end
Then define your raw SQL in lib/sql/active_within_book_on_date.sql. Which would then allow you to do this:
class FixedAsset
def self.active_within_book_on_date(book_id, date)
template = SqlTemplate.new('active_within_book_on_date.sql', binding)
self.find_by_sql(template.sql)
end
end
Your SQL file would look like this:
SELECT a.*
FROM
fixed_assets a
INNER JOIN
(
SELECT e.id, e.fixed_asset_id
FROM
fixed_asset_book_entries e
WHERE e.book_id = <%=book_id%>
) e_mod
ON e_mod.fixed_asset_id = a.id
INNER JOIN
(
SELECT s.fixed_asset_book_entry_id,
s.status,
ROW_NUMBER() OVER (PARTITION BY s.fixed_asset_book_entry_id ORDER BY s.created_at DESC) AS rn
FROM
status_changes s
WHERE s.created_at < '<%=date%>'
) s_mod
ON s_mod.fixed_asset_book_entry_id = e_mod.id AND s_mod.rn = 1 AND s_mod.status <> 'inactive'
ORDER BY a.id;
That's probably as 'nice and clean' as you can get.
I'm having an issue using a column alias for a join in a cte. Invalid column name on the line with RowNumber2 >= (t1.RowNumber - 20) Anyone have a suggestion? Thanks..
DECLARE #latestDate Date = dbo.LatestDateWithPricingVolCountOver4k()
;WITH AllSymbsAndDates AS
(
SELECT
ROW_NUMBER() OVER (PARTITION BY Symbol ORDER BY TradingDate) AS RowNumber,
Symbol, TradingDate
FROM tblSymbolsMain
CROSS JOIN tblTradingDays
WHERE TradingDate <= #latestDate
),
SymbsDatesGrouped AS
(
SELECT * FROM
(
SELECT
t1.Symbol, t1.TradingDate, t2.TradingDate AS TradingDate2, t1.RowNumber,
t2.RowNumber AS RowNumber2
FROM AllSymbsAndDates t1
JOIN AllSymbsAndDates t2 ON t1.Symbol = t2.Symbol
AND RowNumber2 >= (t1.RowNumber - 20)
) t
)
SELECT
Symbol, TradingDate, TradingDate2, RowNumber, RowNumber2
FROM
SymbsDatesGrouped
ORDER BY
Symbol, TradingDate, TradingDate2
You can't reference a column alias in the WHERE or JOIN clauses - actually the only clause where you can reference an alias from the SELECT list is either in the ORDER BY (or in an outer scope, e.g. selecting from a subquery or CTE).
In this case, the solution is pretty trivial. Why not just say:
AND t2.RowNumber >= (t1.RowNumber - 20)
?