PL/SQL fetch records from JOIN clause problem - sql

It's the second question I'm asking for the same issue, so I'll try to be more specific and brief as I can.
In my pl/sql script, i try to insert into a table some records from lots of different tables. I achieve that, except for a field that comes from a JOIN of 2 tables.
The INSERT statement is placed into the following loop:
FOR FST_DEMAND_rec IN (
SELECT *
FROM A
WHERE FK_DEPOSIT_ACCOACC = DEPOSIT_ACC_rec.ACCOUNT_NUMBER
AND TRX_DATE BETWEEN '01-JAN-2018' AND '31-DEC-2018'
ORDER BY TRANS_SER_NUM , ENTRY_SER_NUM ASC)
LOOP
This loop is placed into the following cursor loop :
FOR T IN MOF_CURSOR LOOP
So the INSERT statement, which actually lies in 2 loops, is the following :
INSERT INTO MOF_OUT
VALUES(
T.MOF_REQUEST_ID,
LOC_SERIAL_NUM,
'2B',
CAST(FST_DEMAND_rec.TRX_UNIT || TO_CHAR(FST_DEMAND_rec.TRX_DATE,'YYYYMMDD') || FST_DEMAND_rec.TRX_USR || FST_DEMAND_rec.TRX_SN AS CHAR(50) ),
TO_CHAR(FST_DEMAND_rec.TRX_DATE,'YYYY-MM-DD') ,
CAST(FST_DEMAND_rec.TRX_UNIT AS CHAR(4) ) ,
UNIT_rec.UNIT_NAME ,
CAST(FST_DEMAND_rec.ID_TRANSACT AS CHAR(6) ),
PRFT_TRANS_rec.DESCRIPTION,
-- 'MY PROBLEMATIC FIELD'
TO_CHAR(FST_DEMAND_rec.TIMESTAMP,'hh24miss'),
FST_DEMAND_rec.ENTRY_AMOUNT,
LOC_DC_FLAG,
LOC_PROGR_BALANCE,
LOC_PROGR_BAL_IND
) ;
This INSERT statement works fine, but I don't know how to fill a record which comes from the following SELECT statement with the JOIN clause:
SELECT A.ENTRY_COMMENTS || ' ' || B.DESCRIPTION
FROM FST_DEMAND_EXTRAIT A INNER JOIN JUSTIFIC B ON A.ID_JUSTIFIC = B.ID_JUSTIFIC
For example, the PRFT_TRANS_rec variable that I use in the INSERT, it's declared as PRFT_TRANSACTION%ROWTYPE; and I have the following SELECT statement for it which is within these 2 loops :
SELECT *
INTO PRFT_TRANS_rec
FROM PRFT_TRANSACTION PRFT_TRANS
WHERE PRFT_TRANS.ID_TRANSACT = FST_DEMAND_rec.ID_TRANSACT;

Related

In BigQuery, identify when columns do not match on UNION ALL

with
table1 as (
select 'joe' as name, 17 as age, 25 as speed
),
table2 as (
select 'nick' as name, 21 as speed, 23 as strength
)
select * from table1
union all
select * from table2
In Google BigQuery, this union all does not throw an error because both tables have the same number of columns (3 each). However I receive bad data output because the columns do not match. Rather than outputting a new table with 4 columns name, age, speed, strength with correct values + nulls for missing values (which would probably be preferred), the union all keeps the 3 columns from the top row.
Is there a good way to catch that the columns do not match, rather than the query silently returning bad data? Is there any way for this to return an error perhaps, as opposed to a successful table? I'm not sure how to check in SQL that the columns in the 2 tables match.
Edit: in this example it is clear to see that the columns do not match, however in our data we have 100+ columns and we want to avoid a situation where we make an error in a UNION ALL
Below is for BigQuery Standard SQL and using scripting feature of BQ
DECLARE statement STRING;
SET statement = (
WITH table1_columns AS (
SELECT column FROM (SELECT * FROM `project.dataset.table1` LIMIT 1) t,
UNNEST(REGEXP_EXTRACT_ALL(TRIM(TO_JSON_STRING(t), '{}'), r'"([^"]*)":')) column
), table2_columns AS (
SELECT column FROM (SELECT * FROM `project.dataset.table2` LIMIT 1) t,
UNNEST(REGEXP_EXTRACT_ALL(TRIM(TO_JSON_STRING(t), '{}'), r'"([^"]*)":')) column
), all_columns AS (
SELECT column FROM table1_columns UNION DISTINCT SELECT column FROM table2_columns
)
SELECT (
SELECT 'SELECT ' || STRING_AGG(IF(t.column IS NULL, 'NULL as ', '') || a.column, ', ') || ' FROM `project.dataset.table1` UNION ALL '
FROM all_columns a LEFT JOIN table1_columns t USING(column)
) || (
SELECT 'SELECT ' || STRING_AGG(IF(t.column IS NULL, 'NULL as ', '') || a.column, ', ') || ' FROM `project.dataset.table2`'
FROM all_columns a LEFT JOIN table2_columns t USING(column)
)
);
EXECUTE IMMEDIATE statement;
when applied to sample data from your question - output is
Row name age speed strength
1 joe 17 25 null
2 nick null 21 23
After saving table1 and table2 as 2 tables in a dataset in BigQuery, I then used the metadata using INFORMATION_SCHEMA to check that the columns matched.
SELECT *
FROM models.INFORMATION_SCHEMA.COLUMNS
where table_name = 'table1'
SELECT *
FROM models.INFORMATION_SCHEMA.COLUMNS
where table_name = 'table2'
INFORMATION_SCHEMA.COLUMNS returns information including the column names and their positioning. I can join these 2 tables together then to check that the names match...

How to check if Cursor Expression(select cursor) is returning null?

I've a select cursor expression with multiple inner select cursors. I wanted to check in the outer most select cursor if the inner cursor is returning any value or not.
eg.,
SELECT
CURSOR
(SELECT
CURSOR
(SELECT c.cat_name category ,
CURSOR
(SELECT
CURSOR
(SELECT grp.grpng
GROUPING ,
grp.action_group action_group ,
CURSOR
(SELECT
CURSOR
(SELECT header_id ,
order_no ,
cust_name ,
cust_phone ,
org ,
status `enter code here`,
reason
FROM headers h
WHERE h.header_id = da.header_id ) "G_HEADER"
FROM dual) "LIST_G_HEADER"
FROM action_group grp
WHERE grp.grpng = da.grpng
AND grp.action_grp = da.action_grp ) "G_ACTION"
FROM action_rep da
WHERE da.org = 'test'
AND da.cat_id = c.cat_id
ORDER BY category,
GROUPING,
reason ) "LIST_G_ACTION_GROUP"
FROM dual ) "G_CATEGORY"
FROM dual ) "LIST_G_CATEGORY" ,
c.category_name
FROM cat c
ORDER BY c.cat_name
I need to put a check to see if the result of "LIST_G_CATEGORY" cursor is null or not and if it is null then do not display that row.
P.S: please ignore the column name mismatches as the query is made up in rush to post here.
Version: Oracle 11g R2
In order to check whether the cursor is empty or not, one should fetch it first.
This means that you could crate a function (probably using SYS_REFCURSOR) that will return 1 or 0 accordingly, when the cursor is empty or not - but you should be aware of the rowtype that the cursor returns, in order to do that.
Check this references
oracle cursor expression,
similar question on stackoverflow,
check sys ref cursor is empty - stackoverflw,
ref cursor rowcount question.

Description of the following code?

I do have a situation where a programmer who has developed a stored procedure is absent and doesn't work anymore, so I have to make a little bit of update to the code. I should add 2 more variables in to the code and I know the place, but I don't know how to put it.. Could you please describe the following code, what it does in terms of sql? Cause I know what for this code stand inside the programm..
RAISE NOTICE 'STAT CARD: formid=%',formid;
DROP TABLE IF EXISTS tbl_aggregation;
-- execute
sql_ := 'CREATE TEMPORARY TABLE tbl_aggregation as ' --select programstageinstanceid,'|| dynamic_columns ||' from patientdatavalue'
||'
select OrgUnitID
, max(dateperiod) as dateperiod
, programstageinstanceid
, max("[DE:202062.202063.575]"::integer) as "[DE:202062.202063.575]"
, max(
(case when position(''-'' in "[DE:202062.202063.202095]")>0 then substring("[DE:202062.202063.202095]" from 0 for position(''-'' in "[DE:202062.202063.202095]"))
else "[DE:202062.202063.202095]" end)
) as "[DE:202062.202063.202095]"
, sum("[DE:202062.202063.202078]"::integer) as "[DE:202062.202063.202078]"
, max("[DE:202062.202063.202076]") as "[DE:202062.202063.202076]"
from
(
select psi.organisationunitid as OrgUnitID
, ou.name as OrgName, pdv.timestamp as DatePeriod
, pdv.programstageinstanceid
, pdv.dataelementid
, pdv.value,'
|| dynamic_columns ||'
from patientdatavalue pdv inner join programstageinstance psi
on pdv.programstageinstanceid = psi.programstageinstanceid
-- left join organisationunit ou
inner join tbl_org_list ou
on psi.organisationunitid = ou.organisationunitid
where psi.completed=''t'' and
psi.duedate >= '''||period_from ||''' and
psi.duedate <= '''||period_to ||'''
)as aggregation_
'||cCondition||'
group by programstageinstanceid, OrgUnitID
';
In this code there are hard-coded "variables" as [DE:202062.202063.575], and [DE:202062.202063.202078] and etc.. I do have 2 more different [DE:xxx,xxx,xx] which I should add here.
Because whenever I run this storing procedure it doesn't work and gives the error that those new [DE:xxx,xxx,xx] doesn't exist.. So I should make them exist in the code, but where to put them? TO do it I have to understand the sql syntax and what it does, and unfortunately I'm noob in sql.

Same query but different tables

I'm faced with a big query that is generated in a string and executed with "OPEN pCursor FOR vQuery" and I'm trying to get the query out of the string variable and as a proper "compilable" query.
I'm having this problem where a different table is query depending on a variable
vQuery := 'SELECT ...';
IF pVar = 1 Then
vQuery := vQuery || ' FROM table1';
ELSE
vQuery := vQuery || ' FROM table2';
END IF
vQuery := vQuery || ' WHERE ...';
The two tables have pretty much the same column name. Is there a way to have this as a single query
OPEN Pcursorout FOR
SELECT ... FROM CASE WHEN pVar = 1 THEN table1 ELSE table1 END WHERE ...;
Or I'm stuck at having two queries?
IF pVar = 1 Then
OPEN Pcursorout FOR SELECT ... FROM table1 WHERE ...;
ELSE
OPEN Pcursorout FOR SELECT ... FROM table2 WHERE ...;
END IF
The select and where part are large and exactly the same for both table.
You could use a UNION and use your variable pVar to only include the results from one query in the result set.
SELECT t1.col1, t1.col2, ..., t1.col10
FROM table1 t1
WHERE pVar = 1 and ...
UNION
SELECT t2.col1, t2.col2, ..., t2.col10
FROM table1 t2
WHERE pVar <> 1 and ...
This isn't exactly what you asked about -- not being required to have duplicate lists of columns for the two select statements -- but I think it might capture your intent. It will require that the columns selected by both queries have the same datatype so there will be a (somewhat weak) constraint that the columns of both query results are the same. For example, you won't be able to add a new column to one query but not the other.
Perhaps using UNION / UNION ALL to unite both queries? The requirement for using UNION/UNION ALL is that all SELECTs being united must return columns with the same names.
So if you have
SELECT t.f1,
t.f2,
t.f3
FROM t
WHERE ...
and your other query is
SELECT q.f1,
q.f2,
q.f3
FROM q
WHERE ...
you can have both running as a single SQL statement with UNION:
SELECT t.f1,
t.f2,
t.f3
FROM t
WHERE ...
UNION
SELECT q.f1,
q.f2,
q.f3
FROM q
WHERE ...
Keep in mind that if you need to return columns that exist in one table but not in the other, you can still use UNION, just return NULL and name the column correspondingly to the column name in the table that has it.
Its a bit of a kludge and you might need to look at the performance impact, but you could use an inline view that unions the two base tables, with a flag on each part that you then compare to your variable
SELECT ...
FROM (
SELECT 1 as var, table1.*
FROM table1
UNION ALL
SELECT 2 as var, table2.*
FROM table2
) t
WHERE t.var = pVar
AND ...;
Using an inline view means you don't have to duplicate the main select-list or the where clause etc. If the tables have different columns then you can (and maybe should anyway) only select the columns in the inner queries that will be referenced in the outer select-list.

How to insert bulky value to sql table?

I want to add bulky data from another data below. But I can not do that: error is returned. But areas are the same as another.
declare #hrmtable1 table(musterino int, ekno smallint)
insert into #hrmtable1 (musterino , ekno)
select distinct musterino, ekno
from hareketmuhasebe (nolock)
where islemtarihi >= '20120101'
and isnull(musterino, 0) <> 0
and isnull(musterino, 0) > 9000000
and isnull(ekno,0) <> 0
insert into table1(A,B,C,D,E,. . . . .N)
SELECT DISTINCT
case when ((select count(*) from table1 where musterino=e.musterino) > 0)
then (select top 1 *
from dbo.table1
where musterino = e.musterino
order by ekno desc)
else
(select 100, e.musterino, e.ekno, 0, K, L, M)
from #hrmtable1 e )
end
ERROR:
Msg 120, Level 15, State 1, Line 10
The select list for the INSERT statement contains fewer items than the insert list.
The number of SELECT values must match the number of INSERT columns.
As mentioned in the error the number of columns in insert does not mach the number of value that you are providing
insert into #hrmtable1 (musterino , ekno)
select distinct musterino,ekno
from hareketmuhasebe (nolock)
where islemtarihi >= '20120101'
and isnull(musterino,0) <> 0 and isnull(musterino,0) < 9000000 and isnull(ekno,0) <> 0
Try this:
insert into #hrmtable1 (musterino , ekno)
select distinct musterino,ekno
from hareketmuhasebe (nolock)
where islemtarihi >= '20120101'
and isnull(musterino,0) <> 0 and isnull(musterino,0) < 9000000 and isnull(ekno,0) <> 0
You are having 2 columns in insert & 5 columns in select clause, that is the cause of error.
EDIT: Since you edited the question, still the answer remains the same. Your number of columns in Select statement doesn't match to your insert statement columns in the 2nd part of your code.
In your first statement you are specifying 2 columns and in select you have 5 columns, they should match and that is what the error is telling you exactly.
insert into #hrmtable1 (musterino , ekno)
^^^^^^^ ^^^^
select distinct musterino,ekno,defterid,dovizcinsi, subekodu
^^^^^^^ ^^^ ^^^^ ^^^^^ ^^^^^
From your insert statement it looks like you don't need the last three columns in your Select statement.
The select list for the INSERT statement contains fewer items than the
insert list. The number of SELECT values must match the number of
INSERT columns.
The error itself tells you the story that what you are doing wrong. you have fewer columns in insert statement than to select statement.
From MSDN
The select list of the subquery must match the column list of the
INSERT statement. If no column list is specified, the select list must
match the columns in the table or view being inserted into.
Read more about Inert into