Referencing same random value in a SELECT statement - sql

I have 2 functions I am calling form a SSRS dataset query:
fRndNumber() returns a random value
fCheckSum() returns a calculated value based in the value returned from fRndNumber.
The following statement does not work because the second call of fRndNumber() is generating a different value than the first call. I require the fCheckSum function to use the same value generated by fRndNumber()
Select
fRndNumber() as SerialNumber,
fCheckSum(fRndNumber) as CheckSum
Ideally I would like the statement to read as follows, but of course I cannot reference the 'SerialNumber' in this manner.
Select
fRndNumber() as SerialNumber,
fCheckSum(SerialNumber) as CheckSum
Any suggestions?
Doug P

Depending on your version of SQL, you could use a CTE...
;WITH RandomCte AS
(
SELECT fRndNumber() AS SerialNumber,
* -- add your other columns here...
FROM someTableName -- replace with your table name
)
SELECT fCheckSum(SerialNumber) AS [CheckSum],
*
FROM RandomCte;
See here for more info on CTEs

Using a CTE is considered best practices these days. But if your server doesn't support them, you could also use an inline view:
SELECT
A.SerialNumber,
fCheckSum(A.SerialNumber) AS CheckSum
FROM (
SELECT fRndNumber() as SerialNumber
) as A;

Related

How to Pass in Parameters of CTE in View or Procedure

I have multiple CTEs and I want to pass in two parameters: one being the date and the other being the CTE table name.
This is in oracle db.
The CTEs are like this
WITH big_table1 AS
(
select * from mini_table1
),
big_table2 AS
(
select * from mini_table2
)
The date will be calculated by the select statements in the CTE. I want to do something like select * from &&big_table where &&date = date or any other way (in a view/store procedure) that would help me pass in dynamically the CTE name and a specific date (like '10-23-2021').
I have tried this and it works: select * from &&big_table where &&date = date. But it needs to be in a store proc or view or something that is established I guess. Not really sure why but those are the requirements I need to satisfy.
Appreciate any help.

Adding a single static column to SQL query results

I have a pretty big query (no pun intended) written out in BigQuery that returns about 5 columns. I simply want to append an extra column to it that is not joined to any other table and just returns a single word in every row. As if to be an ID for the entire table.
Just wrap original select and add new constant or add it into original query. The answer might be more precise if you put your query and expected result to your question.
select q.*, 'JOHN' as new_column
from ( <your_big_query> ) q
previous (now unrelated) answer follows
You can use row_number window function:
select q.*, row_number() over (order by null) as id
from ( <your_big_query> ) q
It returns values 1,2, etc.
Depending on how complicated your query is, the row_number could be inlined directly into your query.
If all you want is one static column, just add an extra static column at the end of your existing select columns list.
select {ALL_COLUMNS_YOU_ARE_JOINING_COMPUTING_ETC}, 'something' as your_new_static_col from {YOUR_QUERY}
This static column does not need to be a string, it can be an int or some other type.

Using calculation with an an aliased column in ORDER BY

As we all know, the ORDER BY clause is processed after the SELECT clause, so a column alias in the SELECT clause can be used.
However, I find that I can’t use the aliased column in a calculation in the ORDER BY clause.
WITH data AS(
SELECT *
FROM (VALUES
('apple'),
('banana'),
('cherry'),
('date')
) AS x(item)
)
SELECT item AS s
FROM data
-- ORDER BY s; -- OK
-- ORDER BY item + ''; -- OK
ORDER BY s + ''; -- Fails
I know there are alternative ways of doing this particular query, and I know that this is a trivial calculation, but I’m interested in why the column alias doesn’t work when in a calculation.
I have tested in PostgreSQL, MariaDB, SQLite and Oracle, and it works as expected. SQL Server appears to be the odd one out.
The documentation clearly states that:
The column names referenced in the ORDER BY clause must correspond to
either a column or column alias in the select list or to a column
defined in a table specified in the FROM clause without any
ambiguities. If the ORDER BY clause references a column alias from
the select list, the column alias must be used standalone, and not as
a part of some expression in ORDER BY clause:
Technically speaking, your query should work since order by clause is logically evaluated after select clause and it should have access to all expressions declared in select clause. But without looking at having access to the SQL specs I cannot comment whether it is a limitation of SQL Server or the other RDBMS implementing it as a bonus feature.
Anyway, you can use CROSS APPLY as a trick.... it is part of FROM clause so the expressions should be available in all subsequent clauses:
SELECT item
FROM t
CROSS APPLY (SELECT item + '') AS CA(item_for_sort)
ORDER BY item_for_sort
It is simply due to the way expressions are evaluated. A more illustrative example:
;WITH data AS
(
SELECT * FROM (VALUES('apple'),('banana')) AS sq(item)
)
SELECT item AS s
FROM data
ORDER BY CASE WHEN 1 = 1 THEN s END;
This returns the same Invalid column name error. The CASE expression (and the concatenation of s + '' in the simpler case) is evaluated before the alias in the select list is resolved.
One workaround for your simpler case is to append the empty string in the select list:
SELECT
item + '' AS s
...
ORDER BY s;
There are more complex ways, like using a derived table or CTE:
;WITH data AS
(
SELECT * FROM (VALUES('apple'),('banana') AS sq(item)
),
step2 AS
(
SELECT item AS s FROM data
)
SELECT s FROM step2 ORDER BY s+'';
This is just the way that SQL Server works, and I think you could say "well SQL Server is bad because of this" but SQL Server could also say "what the heck is this use case?" :-)

Use value of a column for another column (SQL Server)?

lets say I have a huge select on a certain table. One value for a column is calculated with complex logc and its called ColumnA. Now, for another column, I need the value from ColumnA and add some other static value to it.
Sample SQL:
select table.id, table.number, complex stuff [ColumnA], [ColumnA] + 10 .. from table ...
The [ColumnA] + 10 is what im looking for. The complex stuff is a huge case/when block.
Ideas?
If you want to reference a value that's computed in the SELECT clause, you need to move the existing query into a sub-SELECT:
SELECT
/* Other columns */,
ColumnA,
ColumnA + 10 as ColumnB
FROM
(select table.id, table.number, complex stuff [ColumnA].. from table ...
) t
You have to introduce an alias for this table (in the above, t, after the closing bracket) even if you're not going to use it.
(Equivalently - assuming you're using SQL Server 2005 or later - you can move your existing query into a CTE):
;WITH PartialResults as (
select table.id, table.number, complex stuff [ColumnA].. from table ...
)
SELECT /* other columns */, ColumnA, ColumnA+10 as ColumnB from PartialResults
CTEs tend to look cleaner if you've got multiple levels of partial computations being done, I.e. if you've now got a calculation that depends on ColumnB to include in your query.
Unfortunately, in SQL Server 2016:
SELECT 3 AS a, 6/a AS b;
Error: Invalid column name: 'a'.
You could solve this with a subquery and column aliases.
Here's an example:
SELECT MaxId + 10
FROM (SELECT Max(t.Id) As MaxId
FROM SomeTable t) As SomeTableMaxId
You could:
Do the + 10 in the client code
Write a scalar-valued function to encapsulate the logic for complex stuff. It will be optimized into a single call.
Copy complex stuff logic for the other column. It should get optimized out into 1 call.
Use a sub-select to apply the additional calculation
One convenient option to reuse scalar expressions in a query is to use APPLY (or LATERAL in standard SQL):
SELECT
table.id,
table.number,
[ColumnA],
[ColumnA] + 10
FROM
table
CROSS APPLY (SELECT complex stuff [ColumnA]) t

SQLServer SQL query with a row counter

I have a SQL query, that returns a set of rows:
SELECT id, name FROM users where group = 2
I need to also include a column that has an incrementing integer value, so the first row needs to have a 1 in the counter column, the second a 2, the third a 3 etc
The query shown here is just a simplified example, in reality the query could be arbitrarily complex, with several joins and nested queries.
I know this could be achieved using a temporary table with an autonumber field, but is there a way of doing it within the query itself ?
For starters, something along the lines of:
SELECT my_first_column, my_second_column,
ROW_NUMBER() OVER (ORDER BY my_order_column) AS Row_Counter
FROM my_table
However, it's important to note that the ROW_NUMBER() OVER (ORDER BY ...) construct only determines the values of Row_Counter, it doesn't guarantee the ordering of the results.
Unless the SELECT itself has an explicit ORDER BY clause, the results could be returned in any order, dependent on how SQL Server decides to optimise the query. (See this article for more info.)
The only way to guarantee that the results will always be returned in Row_Counter order is to apply exactly the same ordering to both the SELECT and the ROW_NUMBER():
SELECT my_first_column, my_second_column,
ROW_NUMBER() OVER (ORDER BY my_order_column) AS Row_Counter
FROM my_table
ORDER BY my_order_column -- exact copy of the ordering used for Row_Counter
The above pattern will always return results in the correct order and works well for simple queries, but what about an "arbitrarily complex" query with perhaps dozens of expressions in the ORDER BY clause? In those situations I prefer something like this instead:
SELECT t.*
FROM
(
SELECT my_first_column, my_second_column,
ROW_NUMBER() OVER (ORDER BY ...) AS Row_Counter -- complex ordering
FROM my_table
) AS t
ORDER BY t.Row_Counter
Using a nested query means that there's no need to duplicate the complicated ORDER BY clause, which means less clutter and easier maintenance. The outer ORDER BY t.Row_Counter also makes the intent of the query much clearer to your fellow developers.
In SQL Server 2005 and up, you can use the ROW_NUMBER() function, which has options for the sort order and the groups over which the counts are done (and reset).
The simplest way is to use a variable row counter. However it would be two actual SQL commands. One to set the variable, and then the query as follows:
SET #n=0;
SELECT #n:=#n+1, a.* FROM tablename a
Your query can be as complex as you like with joins etc. I usually make this a stored procedure. You can have all kinds of fun with the variable, even use it to calculate against field values. The key is the :=
Heres a different approach.
If you have several tables of data that are not joinable, or you for some reason dont want to count all the rows at the same time but you still want them to be part off the same rowcount, you can create a table that does the job for you.
Example:
create table #test (
rowcounter int identity,
invoicenumber varchar(30)
)
insert into #test(invoicenumber) select [column] from [Table1]
insert into #test(invoicenumber) select [column] from [Table2]
insert into #test(invoicenumber) select [column] from [Table3]
select * from #test
drop table #test