Declaring and using variables using WITH clause - sql

I have a query that uses WITH clause (subquery factoring) and i need to define a variable and assign a value to it to use in a couple of the subqueries within the WITH clause.
Is it possible?
Thanks.
I've tried some ways to define the variable and affect it but i always have syntax error.

You can use the following two methods:
1. Use within WITH clause
WITH MY_VARIABLE ( VAL ) AS (
SELECT
12 -- your variable value goes here
FROM
DUAL
), MY_MAIN_WITH_QUERY ( DIFFERENT_COLUMNS ) AS (
SELECT
1
FROM
MY_VARIABLE M
WHERE
M.VAL > 10 -- the use of a variable in your main query
)
SELECT
*
FROM
MY_MAIN_WITH_QUERY;
2. Use DEFINE
DEFINE VAL=12; -- declaring and assigning value to your variable
WITH MY_MAIN_WITH_QUERY ( DIFFERENT_COLUMNS ) AS (
SELECT
1
FROM
DUAL
WHERE
&VAL > 10 -- the use of your variable in your query
)
SELECT
*
FROM
MY_MAIN_WITH_QUERY;
Cheers!!

You can always define your column value in the with clause.
with cte as ( select 1 as your_value from dual )
Then use it to your need. For example:
select your_value from cte

Related

How pass paraeter to WITH clause

I want pass parameter to WITH class something like this:
WITH sction(id) AS (
SELECT q.value1
FROM Example q
WHERE q.id=id )
is it possible?Anyone can help me?
WITH factoring clause a.k.a. CTE (Common Table Expression) is what we some time ago used to call a "subquery". As such, it uses a WHERE clause which you can use to pass that "parameter". For example:
WITH sction AS
(SELECT q.id,
q.value1
FROM Example q
)
SELECT *
FROM sction
WHERE id = 125 --> "125" is that "parameter" you pass while selecting from SCTION CTE
As of a subquery I mentioned: that would have been
select *
from (
select id, value1 from example --> this is a CTE
)
where id = 125
In CTE, it is moved "up", outside your "main" query.
with sction as (select q.value1,q.id from example q )
select * from sction where id = 1;

SQL Table Valued Function - Return certain data from 'WITH' based on parameter value

I am trying to do a "with" to loop through some data (which its doing fine). But after that with, I want to return data dependent on a bit parameter. Its important that this is inside a function. Below is basically what my code is doing.
WITH StuffChain
AS (
//initial
union all
//more
)
After this, I am trying to do something like
CASE WHEN #MyParamVal = 1 THEN
SELECT TOP (1) * FROM StuffChain
ELSE
SELECT * FROM StuffChain
END
RETURN
SQL is not my strength and I am still learning sorry. I am also unsure whether or not to use inline or multi statement function
EDIT: When I am giving the case, I am using it to explain what I am after to return, not necessarily what I will use. I use it to just describe what I need using what little I know if that makes sense.
First of all, using TOP without ORDER BY is somewhat meaningless, because it provides no order against which to select the first few rows. In this case, we can try using ROW_NUMBER to control the ordering:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (ORDER BY some_col) rn
FROM StuffChain
)
SELECT *
FROM cte
WHERE
(rn = #MyParamVal) OR (#MyParamVal <> 1);
You can do as follows. This is just one of the solution. you can do with many other ways also.
WITH StuffChain
AS (
//initial
union all
//more
)
After creation of CTE, try with following
SELECT TOP (CASE WHEN #MyParamVal = 1 THEN 1 ELSE
(SELECT COUNT(1) FROM StuffChain) END *
FROM StuffChain
order by <column> <ASC/DESC>;
We can use a Table Variable e.g. Declare #TEMP Table (intcol int,...) inside a function.
Declare #TEMP Table (intcol int,...)
WITH StuffChain
AS (
//initial
union all
//more
)
SELECT * INTO #TEMP FROM StuffChain;
--do what ever you want with temp table

Return 1 if value is 0 without CASE statement

I'm trying to find a tidier way of doing the below query so that I'm not duplicating my code.
SELECT CASE WHEN <COMPLICATED CODE THAT RETURNS A SINGLE INT> = 0
THEN 1
ELSE <COMPLICATED CODE THAT RETURNS A SINGLE INT> END
Ideally, I would like something like this using an existing function rather than creating my own:
SELECT ISVALUE(COMPLICATED CODE THAT RETURNS A SINGLE INT,0,1)
You can use apply:
SELECT (CASE WHEN v.val = 0 THEN 1 ELSE v.val END)
FROM . . . CROSS APPLY
(VALUES (<COMPLICATED CODE THAT RETURNS A SINGLE INT>)) v(val);
You could also do a series of functions:
select coalesce(nullif(<COMPLICATED CODE THAT RETURNS A SINGLE INT>, 0), 1)
However, I think apply is clearer. In addition the above will turn NULL values into 1 as well as 0.
You can use a CTE (or a subquery) as
WITH CTE AS
(
SELECT <COMPLICATED CODE THAT RETURNS A SINGLE INT> AS Value
FROM ...
)
SELECT CASE WHEN Value = 0 THEN 1 ELSE Value END
FROM CTE
This way you write the complicated code just once, and then use just the Value column.
You can use IIF:
SELECT IIF(1 = 1, 'true', 'false')
https://learn.microsoft.com/en-us/sql/t-sql/functions/logical-functions-iif-transact-sql
use a sub query, then you can figure out a mathematical formula that acts to give the values you desire, that way you can eliminate actual boolean logic and replace with mathematical functions
an example is
SELECT *,(1 - ceiling(cos(atan(abs(cast(x as float)))) -
floor(cos(atan(abs(cast(x as float))))))) +
x * ceiling(cos(atan(abs(cast(x as float)))) -
floor(cos(atan(abs(cast(x as float)))))) as Computed
FROM
( select 0 as x union SELECT 1 X UNION SELECT -.123 UNION SELECT 9.1 UNION SELECT 67000 union select -1) OQ

Select where record does not exists

I am trying out my hands on oracle 11g. I have a requirement such that I want to fetch those id from list which does not exists in table.
For example:
SELECT * FROM STOCK
where item_id in ('1','2'); // Return those records where result is null
I mean if item_id '1' is not present in db then the query should return me 1.
How can I achieve this?
You need to store the values in some sort of "table". Then you can use left join or not exists or something similar:
with ids as (
select 1 as id from dual union all
select 2 from dual
)
select ids.id
from ids
where not exists (select 1 from stock s where s.item_id = ids.id);
You can use a LEFT JOIN to an in-line table that contains the values to be searched:
SELECT t1.val
FROM (
SELECT '1' val UNION ALL SELECT '2'
) t1
LEFT JOIN STOCK t2 ON t1.val = t2.item_id
WHERE t2.item_id IS NULL
First create the list of possible IDs (e.g. 0 to 99 in below query). You can use a recursive cte for this. Then select these IDs and remove the IDs already present in the table from the result:
with possible_ids(id) as
(
select 0 as id from dual
union all
select id + 1 as id from possible_ids where id < 99
)
select id from possible_ids
minus
select item_id from stock;
A primary concern of the OP seems to be a terse notation of the query, notably the set of values to test for. The straightforwwrd recommendation would be to retrieve these values by another query or to generate them as a union of queries from the dual table (see the other answers for this).
The following alternative solution allows for a verbatim specification of the test values under the following conditions:
There is a character that does not occur in any of the test values provided ( in the example that will be - )
The number of values to test stays well below 2000 (to be precise, the list of values plus separators must be written as a varchar2 literal, which imposes the length limit ). However, this should not be an actual concern - If the test involves lists of hundreds of ids, these lists should definitely be retrieved froma table/view.
Caveat
Whether this method is worth the hassle ( not to mention potential performance impacts ) is questionable, imho.
Solution
The test values will be provided as a single varchar2 literal with - separating the values which is as terse as the specification as a list argument to the IN operator. The string starts and ends with -.
'-1-2-3-156-489-4654648-'
The number of items is computed as follows:
select cond, regexp_count ( cond, '[-]' ) - 1 cnt_items from (select '-1-2-3-156-489-4654648-' cond from dual)
A list of integers up to the number of items starting with 1 can be generated using the LEVEL pseudocolumn from hierarchical queries:
select level from dual connect by level < 42;
The n-th integer from that list will serve to extract the n-th value from the string (exemplified for the 4th value) :
select substr ( cond, instr(cond,'-', 1, 4 )+1, instr(cond,'-', 1, 4+1 ) - instr(cond,'-', 1, 4 ) - 1 ) si from (select cond, regexp_count ( cond, '[-]' ) - 1 cnt_items from (select '-1-2-3-156-489-4654648-' cond from dual) );
The non-existent stock ids are generated by subtracting the set of stock ids from the set of values. Putting it all together:
select substr ( cond, instr(cond,'-',1,level )+1, instr(cond,'-',1,level+1 ) - instr(cond,'-',1,level ) - 1 ) si
from (
select cond
, regexp_count ( cond, '[-]' ) - 1 cnt_items
from (
select '-1-2-3-156-489-4654648-' cond from dual
)
)
connect by level <= cnt_items + 1
minus
select item_id from stock
;

Oracle 11g: Default to static value when query returns nothing

Working in Oracle 11g, I have a need to select a datum corresponding to an input value when that value exists in a table, and to instead select a static default value when it does not. The best way I could find to accomplish this was to write something like this:
SELECT desired_datum
FROM (
--Try to get explicit datum
SELECT desired_datum, 1 AS was_found
FROM data_table
WHERE the_key = &input_value
UNION
--Get default datum
SELECT 'default' AS desired_datum, 0 AS was_found
FROM dual
--Put explicit datum on top, if it exists
ORDER BY was_found DESC
) finder
WHERE ROWNUM <=1;
It seems like there must be some idiomatic way to do this which doesn't depend on this strange use of ORDER BY, but I couldn't find it. Does anyone know of any better methods?
You can also do this with aggregation:
SELECT COALESCE(MAX(desired_datum), 'default')
FROM data_table
WHERE the_key = &input_value
This should be a simpler version of what you did:
SELECT NVL(desired_datum, 'default') AS desired_datum
FROM DUAL LEFT JOIN data_table ON the_key = &input_value