T-SQL Why can I reffer only once to temporary object? - tsql

with tmp_rows as
(
select * from [dbo].[customer]
)
select * from tmp_rows;
select count(*) from tmp_rows;
I can't get the count of the tmp_rows because I get the error: Invalid object name 'tmp_rows'
If I comment the "select *" query everything is OK
I need to select all rows and then get their count, how to do that ?

with tmp_rows as
(
select * from [dbo].[customer]
)
select * from tmp_rows;
select ##rowcount;
By declaring you statement in using with you are declaring a CTE - further information on CTE's can be found here

A temporary object created using the with keyword can only be used once. You can create a temporary table if you want to use it more than once:
select *
into #tmp_tows
from dbo.customer
select * from #tmp_rows
select count(*) from #tmp_rows
drop table #tmp_rows
This works even if you want to do something different with the result twice, for example getting the count before the result.

The CTE starting with WITH ends with the semicolon ; .
But you can have more than 1 CTE in a WITH statement:
with tmp_rows as
(
select * from [dbo].customer
),
count_rows as
(
select COUNT(*) count_rows from tmp_rows
)
select * from count_rows, tmp_rows;

tmp_rows is a Common Table Expression (CTE), and CTEs are scoped at the statement level:
-- 1st statement, works fine.
with tmp_rows as (select * from [dbo].[customer])
select * from tmp_rows;
-- 2nd statement, returns an error, since tmp_rows is out of scope.
select count(*) from tmp_rows;
By the time your second statement executes, tmp_rows is already out of scope.
Be aware that CTEs are like locally-scoped views, not tables. The result set is never materialized. If you need to materialize a result set, use a local temporary table or table variable instead.

Related

SELECT ... INTO a variable within a postgres CTE

I want to do this inside a plpgsql function
WITH set1 AS (
select *
from table1
where ... -- reduce table1 to the small working set once for multiple reuse
), query_only_for_select_into AS (
select id
into my_variable_declared_earlier
from set1
where foo = 'bar'
)
select my_variable_declared_earlier as my_bar
, *
from set1
where foo <> 'bar'
but Postgres throws the error
ERROR: SELECT ... INTO is not allowed here
I'm guessing it's because the select ... into is in the CTE. But I can't find anything in the documentation or on the web about it. Maybe I just messed up the select ... into syntax?
SQL has no variables - they are part of a procedural language (e.g. PL/pgSQL), not the query language.
But I don't see the reason why you need one:
WITH set1 AS (
select *
from table1
where ... -- reduce table1 to the small working set once for multiple reuse
), query_only_for_select_into AS (
select id as my_variable_declared_earlier
from set1
where foo = 'bar'
)
select qs.my_variable_declared_earlier as my_bar,
*
from set1
join query_only_for_select_into qs on ...
where foo <> 'bar'
If you are certain that query_only_for_select_into only returns a single row, you can use:
select qs.my_variable_declared_earlier as my_bar,
*
from set1
cross join query_only_for_select_into qs
where foo <> 'bar'
SELECT ... INTO variable is PL/pgSQL syntax, and you can only use it with the top level SELECT (the one that returns results. Think of it like that: PL/pgSQL runs an SQL statement and stores the result somewhere.
But you don't need that: simply include query_only_for_select_into in the FROM clause of the main query, then you can access its columns there.

postgresql WITH statement doesn't find auxiliary statements

I have this query:
WITH words_not AS (
SELECT keywords.id
FROM keywords
WHERE keyword = any(array['writing'])
),actes_not AS (
SELECT actes_keywords.acte_id
FROM actes_keywords
WHERE actes_keywords.keyword_id IN (words_not)
)
SELECT actes.id,
actes.acte_date
FROM actes
WHERE actes.id <> all(actes_not);
This returns the following error:
ERROR: column "words_no" does not exist
LINE 1: ...ctes_keywords WHERE actes_keywords.keyword_id IN (mots_non))...
Each auxiliary statement in the WITH query is good (tested) and I thought I was staying pretty close to the manual: https://www.postgresql.org/docs/current/static/queries-with.html
I don't see why the auxiliary statement in the WITH query is not recognised.
You can't use a table reference in an IN (..) clause. You need a sub-query:
WITH words_not AS (
SELECT keywords.id
FROM keywords
WHERE keyword = any(array['writing'])
), actes_not AS (
SELECT actes_keywords.acte_id
FROM actes_keywords
WHERE actes_keywords.keyword_id IN (select id from words_not) --<< HERE
)
SELECT actes.id,
actes.acte_date
FROM actes
WHERE actes.id <> all(select id from actes_not); --<< HERE

Calling a function on every row returned by a subquery

I need to run the following query to extract the values of my raster records in a specific point.
select st_value((select rast from mytable),
(select st_GeomFromText('POINT(30.424 -1.978)', 4326)))
But I encounter with the following error:
ERROR: more than one row returned by a subquery used as an expression
SQL state: 21000
It needs just one record for this function but I need to extract values of all of records.
If a subquery returns multiple rows, you must either use it in a common table expression (CTE / WITH query) and FROM alias, or use FROM (SELECT ...) alias. In this case, though, it looks like it's simpler than that:
select st_value(rast, st_GeomFromText('POINT(30.424 -1.978)', 4326))
FROM mytable;
Both subqueries appear to be unnecessary.
If you truly needed the subquery you'd write something syntactically like:
WITH sq(rast) AS ( SELECT rast FROM mytable )
SELECT st_value(rast, st_GeomFromText('POINT(30.424 -1.978)', 4326))
FROM sq;
or
SELECT st_value(rast, st_GeomFromText('POINT(30.424 -1.978)', 4326))
FROM (SELECT rast FROM mytable) sq(rast);
Try:
Select st_value(rast),
st_GeomFromText('POINT(30.424 -1.978)', 4326)
from mytable
If you have a function with multiple columns, you can do something like this
SELECT (info).column1, (info).column2, (info).column3
FROM (select st_value(rast, st_GeomFromText('POINT(30.424 -1.978)', 4326)) AS info
FROM mytable
) AS foo

How to make derived column in Oracle and then use it?

How I can make/declare/define a derived column in select query and then use it in where clause?
To define a column in an SQL query, you can use pretty much any SQL operation that returns a single value (including select statements). Here are some examples:
select 'Y' from dual;
select (5 * 3) cal_col from dual;
select (select min(col1) from table 2) calc_col from dual;
select nvl(col1, 'N') has_value from mytable;
From my experience, if you want to use a derived column in a select query, then you must define the column as part of an inner select. Here is an example:
select *
from (
select (col1 * col2) calc_col
from mytable
) data
where data.calc_col > 30
Another alternative is use the calculation within the where clause itself:
select (col1 * col2) calc_col
from mytable t
where (col1 * col2) > 30
If you are performing a count(*) operation, then you can also leverage the HAVING clause:
select field1, count(*)
from mytable
having count(*) > 3

tsql - using internal stored procedure as parameter is where clause

I'm trying to build a stored procedure that makes use of another stored procedure. Taking its result and using it as part of its where clause, from some reason I receive an error:
Invalid object name 'dbo.GetSuitableCategories'.
Here is a copy of the code:
select distinct top 6 * from
(
SELECT TOP 100 *
FROM [dbo].[products] products
where products.categoryId in
(select top 10 categories.categoryid from
[dbo].[GetSuitableCategories]
(
-- #Age
-- ,#Sex
-- ,#Event
1,
1,
1
) categories
ORDER BY NEWID()
)
--and products.Price <=#priceRange
ORDER BY NEWID()
)as d
union
select * from
(
select TOP 1 * FROM [dbo].[products] competingproducts
where competingproducts.categoryId =-2
--and competingproducts.Price <=#priceRange
ORDER BY NEWID()
) as d
and here is [dbo].[GetSuitableCategories] :
if (#gender =0)
begin
select * from categoryTable categories
where categories.gender =3
end
else
begin
select * from categoryTable categories
where categories.gender = #gender
or categories.gender =3
end
I would use an inline table valued user defined function. Or simply code it inline is no re-use is required
CREATE dbo.GetSuitableCategories
(
--parameters
)
RETURNS TABLE
AS
RETURN (
select * from categoryTable categories
where categories.gender IN (3, #gender)
)
Some points though:
I assume categoryTable has no gender = 0
Do you have 3 genders in your categoryTable? :-)
Why do pass in 3 parameters but only use 1? See below please
Does #sex map to #gender?
If you have extra processing on the 3 parameters, then you'll need a multi statement table valued functions but beware these can be slow
You can't use the results of a stored procedure directly in a select statement
You'll either have to output the results into a temp table, or make the sproc into a table valued function to do what you doing.
I think this is valid, but I'm doing this from memory
create table #tmp (blah, blah)
Insert into #tmp
exec dbo.sprocName