Reference a renamed variable with the same name in the source table - sql

I'm trying to reference the new variable DATE_YEAR instead the original DATE_YEAR that I have in my Teradata database TEST.
How can i get it?
I found nothing in Teradata documentation.
SELECT DATE_YEAR+1 AS DATE_YEAR, COUNT(1)
FROM TEST
WHERE DATE_YEAR = 2016 GROUP BY 1;

Based on Standard SQL the columns in the SELECT-list are created after FROM/WHERE/GROUP BY/HAVING/OLAP, but before ORDER BY, so you can use an alias only in ORDER BY.
Due to historical reasons Teradata allows reusage of an alias in any place, this is very convenient as you don't have to cut&paste or use nested SELECTs. But there are some scoping rules, only when a column name is not found it's looked up in the alias-list. So the rough rule of thumb is: Never assign an alias which matches an existing column name and then you can easily use it in any place:
SELECT DATE_YEAR+1 AS DATE_YR, COUNT(1)
FROM TEST
WHERE DATE_YR = 2016 GROUP BY 1;

Wrap it up in a derived table:
select DATE_YEAR_ADJUSTED, count(*)
from
(
SELECT DATE_YEAR+1 AS DATE_YEAR_ADJUSTED
FROM TEST
) as dt
WHERE DATE_YEAR_ADJUSTED = 2016
GROUP BY DATE_YEAR_ADJUSTED
That GROUP BY doesn't make much sense... Only one year anyway.
PS. I don't know Teradata, but I hope this works. ANSI SQL.

Related

BigQuery Standard SQL - Unable to use Alias

My company has recently switched to big query, one issue I am having right now is that big query in standard SQL is not able to accept alias columns in query.
For eg. returns me Unrecognized name: product_code at [3:5].
Does anyone knows a workaround on this issue?
select sales, t_001 as product_code
from "project_01.sales_001.trans_datamart"
where product_code = '001-40040-00'
According to the documentation, you can not reference an alias from the SELECT and list it in a WHERE clause. The where clause filters each row against a bool_expression.
However, there is a way for you to achieve what you want. Below is the syntax:
select sales, product_code
from (select *, t_001 as product_code from "project_01.sales_001.trans_datamart")
where product_code = '001-40040-00'
Therefore, you use the alias as a new column name within your from clause, which makes possible for you to filter using the alias you just created in your where clause.
I would also encourage you to check out this link with all the explanations about aliases in BigQuery.
I'm not aware of any SQL dialect that allows the use of a column alias in the WHERE clause.
Sticking to just the clauses in your example, SQL engines generally evaluate the FROM clause first, determining which tables to pull data from, then evaluate the WHERE clause to filter the retrieved data, and then the SELECT clause to determine what to display and how to display it.
Given that, the column alias is unknown to the SQL engine at the point that it's reading the WHERE clause.
So your options are to either use the column name in the WHERE clause, or, as Gordon suggests in the comments, put the alias in a sub-query or CTE that will be evaluated as part of the FROM clause.
Column name:
select sales, t_001 as product_code
from "project_01.sales_001.trans_datamart"
where t_001 = '001-40040-00' --<--- Modification here.
Sub-query:
select
sales,
product_code
from
(
select sales, t_001 as product_code
from "project_01.sales_001.trans_datamart"
) as d
where product_code = '001-40040-00'

Query working in MySQL but trowing error in Oracle can someone please explain. and tell me how to rewrite this same query in oracle to avoid error [duplicate]

I have a query
SELECT COUNT(*) AS "CNT",
imei
FROM devices
which executes just fine. I want to further restrict the query with a WHERE statement. The (humanly) logical next step is to modify the query followingly:
SELECT COUNT(*) AS "CNT",
imei
FROM devices
WHERE CNT > 1
However, this results in a error message ORA-00904: "CNT": invalid identifier. For some reason, wrapping the query in another query produces the desired result:
SELECT *
FROM (SELECT COUNT(*) AS "CNT",
imei
FROM devices
GROUP BY imei)
WHERE CNT > 1
Why does Oracle not recognize the alias "CNT" in the second query?
Because the documentation says it won't:
Specify an alias for the column
expression. Oracle Database will use
this alias in the column heading of
the result set. The AS keyword is
optional. The alias effectively
renames the select list item for the
duration of the query. The alias can
be used in the order_by_clause but not
other clauses in the query.
However, when you have an inner select, that is like creating an inline view where the column aliases take effect, so you are able to use that in the outer level.
The simple answer is that the AS clause defines what the column will be called in the result, which is a different scope than the query itself.
In your example, using the HAVING clause would work best:
SELECT COUNT(*) AS "CNT",
imei
FROM devices
GROUP BY imei
HAVING COUNT(*) > 1
To summarize, this little gem explains:
10 Easy Steps to a Complete Understanding of SQL
A common source of confusion is the simple fact that SQL syntax
elements are not ordered in the way they are executed. The lexical
ordering is:
SELECT [ DISTINCT ]
FROM
WHERE
GROUP BY
HAVING
UNION
ORDER BY
For simplicity, not all SQL clauses are listed. This lexical ordering
differs fundamentally from the logical order, i.e. from the order of
execution:
FROM
WHERE
GROUP BY
HAVING
SELECT
DISTINCT
UNION
ORDER BY
As a consequence, anything that you label using "AS" will only be available once the WHERE, HAVING and GROUP BY have already been performed.
I would imagine because the alias is not assigned to the result column until after the WHERE clause has been processed and the data generated. Is Oracle different from other DBMSs in this behaviour?

Generate a variable in a SQL statement

I would like to declare a variable in a SQL Oracle statement to work with it in the next lines. I write a simple statement as example:
SELECT customer.surname, LENGTH(customer.name) long, customer.age
FROM customer
WHERE long > 4;
I didn't found any "clear" info on the web, is that even possible?
The order of operations for a select statement is not the same order in which it is written.
FROM (including joins and subqueries but then in the order of operation starts over for that subquery; like order of operations in algebra; inside out )
WHERE
GROUP BY
SELECT
HAVING
ORDER BY
There are some exceptions to the above as not all engines process quite this way. It appears you may be able to use an alias in a group by if you're using mySQL. I'm not familiar enough to know if it changes the processing or if mySQL is just looking ahead.
In this order you can see the where executes before the 'long' alias is generated, so the DB Engine doesn't know what long is at the time it's being executed. Put another way, long is not in scope at the time the where clause is being evaluated.
This can be solved by simply repeating the calculation in the where clause or nesting queries; but the latter is less efficient.
In the below I:
Aliased customer as c to save typing and improve readability.
re-wrote the where clause to use the formula instead of the alias
renamed your long alias due to reserved/keyword use.
.
SELECT c.surname, LENGTH(customer.name) as Name_Len, c.age
FROM customer as c
WHERE LENGTH(c.name)> 4;
In this next example we use the with key word to generate a set of data called CTE (Common Table Expression) with the length of the name calculated. This in effect changes the the order in which the where clause is processed.
In this case the FROM is processed in the CTE then the select including our calculated value but no where clause is applied. Then a second query is run selecting from the CTE data set with the where clause. Since the first dataset already calculated the Name_Len, we can now use it in the where clause.
WITH CTE AS (SELECT c.surname, LENGTH(customer.name) as Name_Len, c.age
FROM customer as c)
SELECT *
FROM CTE
WHERE Name_Len > 4;
This could also be done as a subquery; but after you nest a few of those, you can see using a with may make it easier to read/maintain.
SELECT CTE.*
FROM (SELECT c.surname, LENGTH(customer.name) as Name_Len, c.age
FROM customer as c) as CTE
WHERE CTE.Name_Len > 4;
The way you asked the question is incorrect though there is a solution to your problem in SQL.
SELECT *
FROM (SELECT customer.surname,
LENGTH (customer.name) col_long,
customer.age
FROM customer)
WHERE col_long > 4;
The sub-query here is called in-line view. For more details check Oracle documentation online.
Also, LONG is a reserved keyword, so either rename it or use like "long".
Have you searched online? This is literally covered everywhere... Something like this probably;
DECLARE aVariable NUMBER;
BEGIN
SELECT someColumn INTO aVariable FROM aTable;
END;

Refer to aggregate result in Amazon Redshift query?

In other postgresql DBMSes (e.g., Netezza) I can do something like this without errors:
select store_id
,sum(sales) as total_sales
,count(distinct(txn_id)) as d_txns
,total_sales/d_txns as avg_basket
from my_tlog
group by 1
I.e., I can use aggregate values within the same SQL query that defined them.
However, when I go to do the same sort of thing on Amazon Redshift, I get the error "Column total_sales does not exist..." Which it doesn't, that's correct; it's not really a column. But is there a way to preserve this idiom, rather than restructuring the query? I ask because there would be a lot of code to change.
Thanks.
You simply need to repeat the expressions (or use a subquery or CTE):
select store_id,
sum(sales) as total_sales,
count(distinct txn_id) as d_txns,
sum(sales)/count(distinct txn_id) as avg_basket
from my_tlog
group by store_id;
Most databases do not support the re-use of column aliases in the select. The reason is twofold (at least):
The designers of the database engine do not want to specify the order of processing of expressions in the select.
There is ambiguity when a column alias is also a valid column in a table in the from clause.
Personally I loove the construct in netezza. This is compact and the syntax is not ambiguous: any 'dublicate' column names will default to (new) alias in the current query, and if you need to reference the column of the underlying tables, simply put the tablename in front of the column. The above example would become:
select store_id
,sum(sales) as sales ---- dublicate name
,count(distinct(txn_id)) as d_txns
,my_tlog.sales/d_txns as avg_basket --- this illustrates but may not make sense
from my_tlog
group by 1
I recently moved away from sql server, and on that database I used a construct like this to avoid repeating the expressions:
Select *, total_sales/d_txns as avg_basket
From (
select store_id
,sum(sales) as total_sales
,count(distinct(txn_id)) as d_txns
from my_tlog
group by 1
)x
Most (if not all) databases will support this construct, and have done so for 10 years or more

find group by clause error programmatically

We are going to update our database from 10g to 12c. Before doing so we need to revisit our SQLs, and as a part of this we need to check the following behavior of GROUP BY
case 1:
SELECT abcd_pk,
COUNT(abcd_code),
ABCD_COUNT
FROM
(SELECT abcd_pk,
abcd_code,
(SELECT COUNT(abcd_code) FROM ABCD_TABLE
) "ABCD_COUNT"
FROM ABCD_TABLE
)
GROUP BY abcd_pk, ABCD_COUNT ;
WORKS IN 10G
WORKS IN 12C
case 2:
SELECT abcd_pk,
COUNT(abcd_code),
ABCD_COUNT
FROM
(SELECT abcd_pk,
abcd_code,
(SELECT COUNT(abcd_code) FROM ABCD_TABLE
) "ABCD_COUNT"
FROM ABCD_TABLE
)
GROUP BY abcd_pk ;
WORKS IN 10G
DOES NOT WORK IN 12C (ORA-00979: not a GROUP BY expression 00979. 00000 - "not a GROUP BY expression")
Our code may contains case2 like SQL which is not appropriate. So we need to identify those SQLs. I already prepared a list of SQLs contains GROUP BY used in our project (.java files/procedure/function/views etc.).
Requirement is find the problem in GROUP BY using the following process:
Check the SQLs in the list one by one.
Find an alternate way to check the list of SQLs programmatically.
Option 1 requires massive work load, effort and time, but I think option 2 is not possible.
Is there any suggestion how to proceed?
One possible workaround which might work would be to place the count subquery into an aggregate function, e.g. AVG():
SELECT abcd_pk,
COUNT(abcd_code),
AVG(ABCD_COUNT) -- use an aggregate to not offend Oracle
FROM
(
SELECT abcd_pk,
abcd_code,
(SELECT COUNT(abcd_code) FROM ABCD_TABLE) "ABCD_COUNT"
FROM ABCD_TABLE
)
GROUP BY abcd_pk;
If this fixes the problem, then it means that Oracle's treatment of subqueries resulting in constants has changed between 10G and 12C.