Oracle SQL use subquery value in another subquery - sql

Before I go any further please mind that I am not well experienced with SQL.
I have one query that is getting a single value (netto value) such as:
WITH cte_value_net AS (
SELECT
nvl(min(value_net),0) as value_net
FROM (
SELECT
i.serial as serial,
nvl(lag(i.value_net) OVER (PARTITION BY i.serial ORDER BY i.month), i.value_net) as value_net
FROM
inventory i
WHERE
i.ctypde IN (
SELECT
ctypde
FROM
appar ap
WHERE
ap.serial = in_serial -- this is the variable I want to set
)
AND
i.month IN (to_char(add_months(sysdate, -1), 'YYYYMM'), to_char(add_months(sysdate, -2), 'YYYYMM'))
AND
i.serial = in_serial -- this is the variable I want to set
) vn
GROUP BY vn.serial
)
In here I have to feed in the variable in_serial that I thought I could get from another subquery such as:
SELECT
(SELECT * FROM cte_value_net) AS value_net
FROM (
SELECT
lap.serial AS in_serial
FROM
applap lap
)
but I can not wrap my head around it why this in_serial is not visible to my custom CTE. Could someone explain me how can I propagate the value from subquery like this?
The error I am obviously getting is:
SQL Error [904] [42000]: ORA-00904: "IN_SERIAL"
Unfortunately I do not have any sample data. What I want to achieve is that I could feed in the returned in_serial from main subquery to my CTE.
Before I can get value_net I need my main query to return the in_serial, otherwise I do not have access to that value.

The trick I use is to produce an extra CTE that I usually call params that includes a single row with all computed parameters. Then, it's a matter of performing a CROSS JOIN with this CTE in any other CTE, subquery or main query, as needed.
For example:
with
params as ( -- 1. Create a CTE that returns a single row
select serial as in_serial from applap
),
cte_value_net AS (
select ...
from inventory i
cross join params -- 2. cross join against the CTE anywhere you need it
where ...
and i.serial = params.in_serial -- 3. Use the parameter
)
select ...

First, your second query is not syntactically correct, as there's a ',' before the FROM. You can write your query like this:
WITH cte_value_net AS (
SELECT
serial, nvl(min(value_net),0) as value_net
FROM (
SELECT
i.serial as serial,
nvl(lag(i.value_net) OVER (PARTITION BY i.serial ORDER BY i.month), i.value_net) as value_net
FROM
inventory i
WHERE
i.ctypde IN (
SELECT
ctypde
FROM
appar ap
WHERE
ap.serial = i.serial
)
AND
i.month IN (to_char(add_months(sysdate, -1), 'YYYYMM'), to_char(add_months(sysdate, -2), 'YYYYMM'))
) vn
GROUP BY vn.serial
)
select ...
from cte_value_net s join applap lap on (lap.serial=s.serial)
(adjust query to your schema ....)

Related

Apache Phoenix SQL Join Limitation when using sub-queries

I have this query in Apache Phoenix SQL:
select WO.* from (
select "nr_id", "txt_commrcial_label"
from "e_application" APP
where "txt_commrcial_label" in ('a','b')
and "nr_id" not in (select "nr_ap_id"
from "e_workorder"
where "nr_id" in ('888'))
and "epochtimestampchanged" = (select max("epochtimestampchanged")
from "e_application"
where "nr_id" = APP."nr_id") ) as APP2,
--
(select Y.ID as WO_ID, Y."nr_id" as WO_nr_id, Y."nr_ap_id" as WO_nr_ap_id
from ( select "nr_id", max("epochtimestampchanged") as max_epochtimestampchanged
from "e_workorder"
where CAST(TO_NUMBER("epochtimestampchanged") AS TIMESTAMP) < TO_TIMESTAMP('2020-10-21 19:22:20.0')
group by "nr_id" ) as X, "e_workorder" as Y
where Y."nr_id" = X."nr_id"
and Y."epochtimestampchanged" < X.max_epochtimestampchanged ) as WO
--
where APP2."nr_id" = WO.WO_nr_ap_id;
I get java language illegal ... blurb for this not overly complex statement. But I cannot see the reason here or in the manuals.
The individual queries work (imagine the ( and , are not there), but no joy when these 2 sub-queries merged to a JOIN.
Do I need to persist the results to tables and then JOIN? Or is there way around this? I have the impression this is too complex in terms of sub-queries.
For others to note, this is a big and a different SQL Approach is needed as per below which is a work-around with note from Cloudera:
The best workaround is to explicitly define a join in the APP2 query.
See the APP_MAX_TIMESTAMP table joined with the APP table, defining
basically the same condition as in the original query (but using a
table join instead of an inner select):
The query that should work and should do the same as the original
query:
select
WO.*
from
(
select
"nr_id",
"txt_commrcial_label"
from
"e_application" APP
LEFT JOIN (
select
max("epochtimestampchanged") as max_app_timestamp,
"nr_id" as max_app_timestamp_nr_id
from
"e_application"
group by "nr_id"
) APP_MAX_TIMESTAMP
ON APP_MAX_TIMESTAMP.max_app_timestamp_nr_id = APP."nr_id"
where
"txt_commrcial_label" in
( list
)
and "nr_id" not in
(
select
"nr_ap_id"
from
"e_workorder"
where
"nr_id" in
(
'888'
)
)
and "epochtimestampchanged" = max_app_timestamp
)
as APP2,
(
select
Y.ID as WO_ID,
Y."nr_id" as WO_nr_id,
Y."nr_ap_id" as WO_nr_ap_id
from
(
select
"nr_id",
max("epochtimestampchanged") as max_epochtimestampchanged
from
"e_workorder"
where
CAST(TO_NUMBER("epochtimestampchanged") AS TIMESTAMP) < TO_TIMESTAMP('2022-10-10 19:22:20.0')
group by
"nr_id"
)
as X,
"e_workorder" as Y
where
Y."nr_id" = X."nr_id"
and Y."epochtimestampchanged" < X.max_epochtimestampchanged
)
as WO
where
APP2."nr_id" = WO.WO_nr_ap_id;

ORACLE SQL Pivot Issue

I am trying to pivot a sql result. I need to do this all in the one query. The below is telling me invalid identifier for header_id. I am using an Oracle database.
Code
Select * From (
select ppd.group_id,g.group_name, ct.type_desc,ht.hos_cat_descr
from item_history ih, item ci, contract ppd,
header ch, group g, cd_std_type ct, cd_hos h,
cd_std_hospital_cat ht
where ih.item_id = ci.item_id
and ih.header_id = ch.header_id
and ci.hos_id = h.hos_id
and ih.item_id = ci.item_id
and ch.user_no = ppd.user_no
and ppd.group_id = g.group_id
and ch.header_type = ct.header_type_id
and ci.hos_id = h.hos_id
and h.cat_id = ht.cat_id
)
Pivot
(
count(distinct header_id) as Volume
For hos_cat_descr IN ('A')
)
Your inner query doesn't have header_id in its projection, so the pivot clause doesn't have that column available to use. You need to add it, either as:
Select * From (
select ppd.group_id,g.group_name, ct.type_desc,ht.hos_cat_descr,ih.header_id
---------------------------------------------------------------^^^^^^^^^^^^^
from ...
)
Pivot
(
count(distinct header_id) as Volume
For hos_cat_descr IN ('A')
)
or:
Select * From (
select ppd.group_id,g.group_name, ct.type_desc,ht.hos_cat_descr,ch.header_id
---------------------------------------------------------------^^^^^^^^^^^^^
from ...
)
Pivot
(
count(distinct header_id) as Volume
For hos_cat_descr IN ('A')
)
It doesn't really matter which, since those two values must be equal as they are part of a join condition.
You could achieve the same thing with simpler aggregation instead of a pivot, but presumably you are doing more work in the pivot really.

Temporary table in FROM clause instead of WITH clause

I have this query (which runs perfect):
WITH TEMPTABLE AS(
SELECT count(CONTRACT_ID) AS no_contract, T.BRANCH_ID as branch
FROM CONTRACT, "EMPLOYMENT" T, TABLE(T."EMPLOYEE") TT
WHERE CONTRACT.EMPLOYEE_ID = TT.EMPLOYEE_ID
AND CONTRACT.CONTRACT_VT_START BETWEEN '01/01/2018' AND '30/06/2018'
GROUP BY T.BRANCH_ID
)
SELECT branch
FROM TEMPTABLE
WHERE no_contract IN (SELECT MAX(no_contract)
FROM TEMPTABLE);
And I was wondering if it can be done with another way instead of WITH clause. Something like this:
SELECT count_per_branch.BRANCH
FROM (SELECT count(CONTRACT_ID) AS no_contract, BRANCH_ID AS BRANCH
FROM CONTRACT, "EMPLOYMENT" T, TABLE(T."EMPLOYEE") TT
WHERE CONTRACT.EMPLOYEE_ID = TT.EMPLOYEE_ID
AND CONTRACT.CONTRACT_VT_START BETWEEN '01/01/2018' AND '30/06/2018'
GROUP BY T.BRANCH_ID) count_per_branch
WHERE count_per_branch.no_contract =
(SELECT max(count_per_branch.no_contract)
FROM count_per_branch);
Which gives an ORA-00942: "table or view does not exist"
Error at Line: 9
You can use a subquery in the FROM clause:
FROM (SELECT count(CONTRACT_ID) ... )
And you can give it an alias:
FROM (SELECT count(CONTRACT_ID) ... ) count_per_branch
And you can use that alias to qualify column references:
WHERE count_per_branch.no_contract =
But you cannot use that alias as a row source in another subquery, which is what you are trying to do here:
(SELECT max(count_per_branch.no_contract)
FROM count_per_branch)
To "reuse" a subquery in this manner, you have to use the Common Table Expression syntax -- i.e., the WITH clause.

Syntax error in FROM clause - MS ACCESS

I am working with a tool that would extract some data from an Access Database. So basically, i am working on a query to get this data.
Below is the code i am currently working on.
I am getting an error: Syntax error in FROM clause
I can't seem to find where the query is going wrong. I would appreciate any help! Thank youu.
EDIT: putting my actual query
SELECT table_freq.*, IIF(table_freq.txn_ctr > (table_ave_freq.ave_freq * 3), "T", "F") as suspicious_flag
FROM
(
SELECT tbl_TransactionHistory.client_num, tbl_TransactionHistory.client_name,
tbl_TransactionHistory.transaction_date, Count(tbl_TransactionHistory.client_num) AS txn_ctr
FROM tbl_TransactionHistory
GROUP BY tbl_TransactionHistory.client_num, tbl_TransactionHistory.client_name,
tbl_TransactionHistory.transaction_date
) AS table_freq
INNER JOIN
(
SELECT table_total_freq.client_num, total_txn_ctr as TotalTransactionFrequency, total_no_days as TotalTransactionDays,
(table_total_freq.total_txn_ctr)/(table_no_of_days.total_no_days) AS ave_freq
FROM
(
(
SELECT client_num, SUM(txn_ctr) AS total_txn_ctr
FROM
(
SELECT client_num, client_name, transaction_date, COUNT(client_num) AS txn_ctr
FROM tbl_TransactionHistory
GROUP BY client_num, client_name, transaction_date
) AS tabFreq
GROUP BY client_num
) AS table_total_freq
INNER JOIN
(
SELECT client_num, COUNT(txn_date) as total_no_days
FROM
(
SELECT DISTINCT(transaction_date) as txn_date, client_num
FROM tbl_TransactionHistory
ORDER BY client_num
) AS table1
GROUP BY client_num
) AS table_no_of_days
ON table_total_freq.client_num = table_no_of_days.client_num
)
) AS table_ave_freq
ON table_freq.client_num = table_ave_freq.client_num

Reuse subquery result in WHERE-Clause for INSERT

i am using Microsoft SQL Server 2008
i would like to save the result of a subquery to reuse it in a following subquery.
Is this possible?
What is best practice to do this? (I am very new to SQL)
My query looks like:
INSERT INTO [dbo].[TestTable]
(
[a]
,[b]
)
SELECT
(
SELECT TOP 1 MAT_WS_ID
FROM #TempTableX AS X_ALIAS
WHERE OUTERBASETABLE.LT_ALL_MATERIAL = X_ALIAS.MAT_RM_NAME
)
,(
SELECT TOP 1 MAT_WS_NAME
FROM #TempTableY AS Y_ALIAS
WHERE Y_ALIAS.MAT_WS_ID = MAT_WS_ID
--(
--SELECT TOP 1 MAT_WS_ID
--FROM #TempTableX AS X_ALIAS
--WHERE OUTERBASETABLE.LT_ALL_MATERIAL = X_ALIAS.MAT_RM_NAME
--)
)
FROM [dbo].[LASERTECHNO] AS OUTERBASETABLE
My question is:
Is this correct what i did.
I replaced the second SELECT Statement in the WHERE-Clause for [b] (which is commented out and exactly the same as for [a]), with the result of the first SELECT Statement of [a] (=MAT_WS_ID).
It seems to give the right results.
But i dont understand why!
I mean MAT_WS_ID is part of both temporary tables X_ALIAS and Y_ALIAS.
So in the SELECT statement for [b], in the scope of the [b]-select-query, MAT_WS_ID could only be known from the Y_ALIAS table. (Or am i wrong, i am more a C++, maybe the scope things in SQL and C++ are totally different)
I just wannt to know what is the best way in SQL Server to reuse an scalar select result.
Or should i just dont care and copy the select for every column and the sql server optimizes it by its own?
One approach would be outer apply:
SELECT mat.MAT_WS_ID
, (
SELECT TOP 1 MAT_WS_NAME
FROM #TempTableY AS Y_ALIAS
WHERE Y_ALIAS.MAT_WS_ID = mat.MAT_WS_ID
)
FROM [dbo].[LASERTECHNO] AS OUTERBASETABLE
OUTER APPLY
(
SELECT TOP 1 MAT_WS_ID
FROM #TempTableX AS X_ALIAS
WHERE OUTERBASETABLE.LT_ALL_MATERIAL = X_ALIAS.MAT_RM_NAME
) as mat
You could rank rows in #TempTableX and #TempTableY partitioning them by MAT_RM_NAME in the former and by MAT_WS_ID in the latter, then use normal joins with filtering by rownum = 1 in both tables (rownum being the column containing the ranking numbers in each of the two tables):
WITH x_ranked AS (
SELECT
*,
rownum = ROW_NUMBER() OVER (PARTITION BY MAT_RM_NAME ORDER BY (SELECT 1))
FROM #TempTableX
),
y_ranked AS (
SELECT
*,
rownum = ROW_NUMBER() OVER (PARTITION BY MAT_WS_ID ORDER BY (SELECT 1))
FROM #TempTableY
)
INSERT INTO dbo.TestTable (a, b)
SELECT
x.MAT_WS_ID,
y.MAT_WS_NAME
FROM dbo.LASERTECHNO t
LEFT JOIN x_ranked x ON t.LT_ALL_MATERIAL = x.MAT_RM_NAME AND x.rownum = 1
LEFT JOIN y_ranked y ON x.MAT_WS_ID = y.MAT_WS_ID AND y.rownum = 1
;
The ORDER BY (SELECT 1) bit is a trick to specify an indeterminate ordering, which, accordingly, would result in indeterminate rownum = 1 rows picked by the query. That is to more or less duplicate your TOP 1 without an explicit order, but I would recommend you to specify a more sensible ORDER BY clause to make the results more predictable.