Selection to join the tables VBFA and VBRK - abap

I have done a selection of the table VBFA, that has selected various SD documents based on the code below, and another selection of the table VBRK that has selected the documents that do not have a NETWR = 0. What I want to achieve is to select only those SD documents that are NETWR NE 0.
The code for the selection of the table VBFA is as follows:
SELECT * FROM vbfa AS v INTO TABLE gt_vbfa_inv
FOR ALL ENTRIES IN gt_vbak
WHERE vbelv = gt_vbak-vbeln
AND vbtyp_n IN ('M', 'O', 'P', '5', '6')
AND stufe = '00'
AND NOT EXISTS ( SELECT * FROM vbfa
WHERE vbelv = v~vbeln
AND posnv = v~posnn
AND vbtyp_n IN ('N', 'S')
AND stufe = '00' ) .
And the code for the selection of the VBRK table is as follows:
IF sy-subrc = 0.
SELECT DISTINCT * FROM vbrk
INTO TABLE gt_vbrk
FOR ALL ENTRIES IN gt_vbfa_inv
WHERE vbeln EQ gt_vbfa_inv-vbeln
AND netwr NE 0.
ENDIF.
Is there any way to merge these to two selection via a new Select or a Loop condition, that will select the documents of the table VBFA, where the documents will not have a NETWR = 0 (or NETWR NE 0)?
Thank you all in advance!

My standard approach for converting code using FOR ALL ENTRIES to a JOIN is this.
Convert it to the new SQL syntax with # in front of every variable.
Replace the SELECT * with the fields I actually need. This is important, because JOINs between two tables will always have duplicate field. So SELECT * doesn't work. And it's a bad practice anyway. You almost never need all the fields, so SELECT * is almost always a waste of memory and CPU cycles.
Take both SELECTs and prefix every column name with the table. For example AND stufe = '00' becomes AND vbfa~stufe = '00'
Take the FOR ALL ENTRIES from the second and turn it into a JOIN-condition on the first select. So
FROM vbrk
FOR ALL ENTRIES IN #gt_vbfa_inv
WHERE vbrk~vbeln = #gt_vbfa_inv-vbeln
AND vbrk~netwr <> 0.
becomes
JOIN vbrk
ON vbrk~vbeln = vbfa~vbeln AND
vbrk~netwr <> 0.
Use an inline data declaration for the result-set. That way you don't need to manually create a table- and structure type for holding the results. The end-result would thus look something like this (untested code):
SELECT vbfa~somefield1
vbfa~somefield2
vbfa~somefield3
vbrk~somefield1
vbrk~somefield2
FROM vbfa
JOIN vbrk ON
vbrk~vbeln = vbfa~vbeln AND
vbrk~netwr NE 0.
INTO TABLE #DATA(gt_results)
FOR ALL ENTRIES IN #gt_vbak
WHERE vbfa~vbelv = gt_vbak-vbeln
AND vbfa~vbtyp_n IN ('M', 'O', 'P', '5', '6')
AND vbfa~stufe = '00'
AND NOT EXISTS ( SELECT * FROM vbfa AS vbfa2
WHERE vbfa2~vbelv = vbfa~vbeln
AND vbfa2~posnv = vbfa~posnn
AND vbfa2~vbtyp_n IN ('N', 'S')
AND vbfa2~stufe = '00' ) .
By the way: If gt_vbak comes from a SELECT too, then you likely can integrate this as another join here, too.

Related

How to write an Open SQL statement with substring in the JOIN ON condition? [duplicate]

I have the following select statement in ABAP:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
INTO corresponding fields of table GT_INSTMUNIC_F
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN EVER AS EV on
MUNIC~POD = EV~VREFER(9).
"where EV~BSTATUS = '14' or EV~BSTATUS = '32'.
My problem with the above statement is that does not recognize the substring/offset operation on the 'ON' clause. If i remove the '(9) then
it recognizes the field, otherwise it gives error:
Field ev~refer is unknown. It is neither in one of the specified tables
nor defined by a "DATA" statement. I have also tried doing something similar in the 'Where' clause, receiving a similar error:
LOOP AT gt_instmunic.
clear wa_gt_instmunic_f.
wa_gt_instmunic_f-mandt = gt_instmunic-mandt.
wa_gt_instmunic_f-bis = gt_instmunic-bis.
wa_gt_instmunic_f-ab = gt_instmunic-ab.
wa_gt_instmunic_f-zzelecdate = gt_instmunic-zzelecdate.
wa_gt_instmunic_f-ZZCERTDATE = gt_instmunic-ZZCERTDATE.
wa_gt_instmunic_f-CONSYEAR = gt_instmunic-CONSYEAR.
wa_gt_instmunic_f-ZDIMO = gt_instmunic-ZDIMO.
wa_gt_instmunic_f-ZZONE_M = gt_instmunic-ZZONE_M.
wa_gt_instmunic_f-ZZONE_T = gt_instmunic-ZZONE_T.
wa_gt_instmunic_f-USAGE_M = gt_instmunic-USAGE_M.
wa_gt_instmunic_f-USAGE_T = gt_instmunic-USAGE_T.
temp_pod = gt_instmunic-pod.
SELECT vrefer
FROM ever
INTO wa_gt_instmunic_f-vrefer
WHERE ( vrefer(9) LIKE temp_pod ). " PROBLEM WITH SUBSTRING
"AND ( BSTATUS = '14' OR BSTATUS = '32' ).
ENDSELECT.
WRITE: / sy-dbcnt.
WRITE: / 'wa is: ', wa_gt_instmunic_f.
WRITE: / 'wa-ever is: ', wa_gt_instmunic_f-vrefer.
APPEND wa_gt_instmunic_f TO gt_instmunic_f.
WRITE: / wa_gt_instmunic_f-vrefer.
ENDLOOP.
itab_size = lines( gt_instmunic_f ).
WRITE: / 'Internal table populated with', itab_size, ' lines'.
The basic task i want to implement is to modify a specific field on one table,
pulling values from another. They have a common field ( pod = vrefer(9) ). Thanks in advance for your time.
If you are on a late enough NetWeaver version, it works on 7.51, you can use the OpenSQL function LEFT or SUBSTRING. Your query would look something like:
SELECT munic~mandt VREFER BIS AB ZZELECDATE ZZCERTDATE CONSYEAR ZDIMO ZZONE_M ZZONE_T USAGE_M USAGE_T M2MC M2MT M2RET EXEMPTMCMT EXEMPRET CHARGEMCMT
FROM ZCI00_INSTMUNIC AS MUNIC
INNER JOIN ever AS ev
ON MUNIC~POD EQ LEFT( EV~VREFER, 9 )
INTO corresponding fields of table GT_INSTMUNIC_F.
Note that the INTO clause needs to move to the end of the command as well.
field(9) is a subset operation that is processed by the ABAP environment and can not be translated into a database-level SQL statement (at least not at the moment, but I'd be surprised if it ever will be). Your best bet is either to select the datasets separately and merge them manually (if both are approximately equally large) or pre-select one and use a FAE/IN clause.
They have a common field ( pod = vrefer(9) )
This is a wrong assumption, because they both are not fields, but a field an other thing.
If you really need to do that task through SQL, I'll suggest you to check native SQL sentences like SUBSTRING and check if you can manage to use them within an EXEC_SQL or (better) the CL_SQL* classes.

Validating Phone Numbers in Batch with PostgreSQL

This is my SQL:
SELECT
countries.locl_ctry_id,
countries.icc,
countries.active,
networks.locl_ntwrk_id,
networks.locl_ctry_id,
numberings.locl_ntwrk_id,
numberings.ndc,
numberings.size
FROM countries
LEFT JOIN networks
ON networks.locl_ctry_id = countries.locl_ctry_id
LEFT JOIN numberings
ON numberings.locl_ntwrk_id = networks.locl_ntwrk_id
WHERE
countries.active = 'true'
AND numberings.locl_ntwrk_id NOTNULL
AND CONCAT(countries.icc, numberings.ndc)
LIKE LEFT('381645554330', CHAR_LENGTH(CONCAT(countries.icc, numberings.ndc)))
AND LENGTH('381645554330') = numberings.size
I would like to run this script for a batch of numbers, for example:
381645554330 ‭
381629000814‬‬
381644446555‬
‭38975300155‬
‭38975604099 ‭
38976330923‬‬ ‭
38977772090‬ ‭
38978250177‬ ‭
38970333730‬
‭38971388262‬
‭38972228855‬
Take a look at the database structure here: http://sqlfiddle.com/#!17/13ce29/27
It needs to validate the Prefix as well as the Length of the number.
Any suggestions how to achieve this?
Put the batch of numbers in a union all subquery.
SELECT
countries.locl_ctry_id,
countries.icc,
countries.active,
networks.locl_ntwrk_id,
networks.locl_ctry_id,
numberings.locl_ntwrk_id,
numberings.ndc,
numberings.size
FROM countries
LEFT JOIN networks
ON networks.locl_ctry_id = countries.locl_ctry_id
LEFT JOIN numberings
ON numberings.locl_ntwrk_id = networks.locl_ntwrk_id
JOIN ( select '381645554330' as num
union all
select '38976330923‬‬‬‬'
union all
select '38975300155‬‬‬' ) batch_numbers
ON CONCAT(countries.icc, numberings.ndc)
LIKE LEFT(batch_numbers.num, CHAR_LENGTH(CONCAT(countries.icc, numberings.ndc)))
AND LENGTH(batch_numbers.num) = numberings.size
WHERE
countries.active = 'true'
AND numberings.locl_ntwrk_id NOTNULL
It seems the objective is not ability to return the set of values currently returned by the single, but to make an evaluation of the of multiple values. The issue with the above it requires an a-priori knowledge of and a modification to the query for each set to evaluate. The follow will attempt to remove that requirement.
Let's begin by developing a base line query as an extension to Jakup's "union" solution.
--- create a baseline solution
with to_be_validated (test_num) as -- CTE used strictly as data generator fir query
( values ('381645554330')
, ('381629000814')
, ('381644446555')
, ('38975300155')
, ('38975604099')
, ('38976330923')
, ('38977772090')
, ('38978250177')
, ('38970333730')
, ('38971388262')
, ('38972228855')
, ('81771388262')
, ('55572228855')
)
--- base query
select test_num
, case when icc is not null then 'Valid' else 'Invalid' end validation
from to_be_validated
left join(
select countries.icc, numberings.ndc, numberings.size
from countries
join networks on networks.locl_ctry_id = countries.locl_ctry_id
join numberings on numberings.locl_ntwrk_id = networks.locl_ntwrk_id
) base on ( concat(base.icc, base.ndc) = left( test_num, char_length(concat(base.icc, base.ndc)))
and length(test_num) = base.size
)
;
Notes on Query and Modifications:
1. the column countries.active is defined as binary, thus already providing a True/False value. Thus checking for "= 'true' is unnecessary. Altered to just contries.active.
2. The column numberings.locl_ntwrk_id is restricted to being NOT NULL, so the Predicate "nullnumberings.locl_ntwrk_id NOTNULL" is always true. Removed predicate.
3. The LEFT JOIN on networks and numberings will generate a result set with all countries, all networks, all numberings, even when the combination is itself invalid. This results in validating each phone number against every combination of the 3 base tables. Alter these inner joins.
4. Finally, I added a couple extra numbers to your test data. These are intended to fail the desired validation. You should always test with considerable invalid data, otherwise you cannot know if procedure/query/whatever gracefully and properly handles it.
Now with a base query in hand it's possible to just end here. But to be generally useful you cannot edit the query each time it wanted. Therefore lets wrap a function definition around it. We'll do this by wrapping a function definition around that base query, and provide either an array or a delimited string containing the phone numbers to b evaluated.
In each the base query remains the same, and we keep the CTE, but the CTE is modified to build a row for each phone number provided.
-- SQL Function with and Array input
create or replace function validate_phone_numbers( phone_numbers text[])
returns table ( phone_number text
, validation_status text
)
language sql
as $$
with to_be_validated as
( select unnest (phone_numbers) test_num )
-- Insert base query here --
$$
-- Test with Array
select phone_number, validation_status
from validate_phone_numbers (ARRAY
[ ('381629000814')
, ('381644446555')
, ('38975300155')
, ('38975604099')
, ('38976330923')
, ('38977772090')
, ('38978250177')
, ('38970333730')
, ('38971388262')
, ('38972228855')
, ('81771388262')
, ('55572228855')
]
) ;
With a minor extension we a delimited string version.
create or replace function validate_phone_numbers_with_string( phone_numbers text, delimiter text default ',')
returns table ( phone_number text
, validation_status text
)
language sql
as $$
with to_be_validated as
( select unnest (string_to_array (phone_numbers, delimiter)) test_num)
-- Insert base query here --
$$ ;
-- test with string
select phone_number, validation_status
from validate_phone_numbers_with_string('381629000814,381644446555,38975300155,38975604099,38976330923,38977772090,38978250177,38970333730,38971388262,38972228855,81771388262,55572228855');

In SQL , how do I JOIN a column that is usually null so that the data is still retrieved?

I want to incorporate a new column into a working SQL query.
However, it causes the whole query return nothing at all(because the column is mostly null in the database) .
Here's my pared-down code so far :
SELECT DISTINCT submittedRow.PERFORMED_DATE as "submitted",
supervisorRow.PERFORMED_DATE as "superv",
/* coalesce(sodRow.PERFORMED_DATE, TO_DATE('2000/07/07', 'YYYY/MM/DD') ) */ null AS "SOD"
hhs_umx_resp_activity submittedRow
join hhs_umx_resp_activity supervisorRow ON supervisorRow.reg_request_id = configRow.reg_request_id
/* join hhs_umx_resp_activity sodRow ON sodRow.reg_request_id = approvedRow.reg_request_id */
left join HHS_UMX_REG_REQUESTS hurr on hurr.reg_request_id = hur.reg_request_id
WHERE
and supervisorRow.ACTIVITY_RESULT_CODE = 'ASP'
AND submittedRow.activity_result_code = 'SBT'
/* AND sodRow.activity_result_code = 'ASD'*/
and hur.REG_REQUEST_ID IN ('262097')
The column that is mostly null, which I want to add in, is sodRow ( that's why the code AND sodRow.activity_result_code = 'ASD' is commented ).
Whenever I put back the extra join for sodRow , it just nulls out everything and I get no results at all. But I want it to work like a NVL or COALESCE, where it only displays that column if it exists, and otherwise just shows everything else.
I tried to create a view first in the code, then to do UNION on it. But it seems like view are only for PL/SQL code.
I also tried the outer joins, but this doesn't work.
I think the problem may be in the WHERE condition of my join code. I did like Dmitri suggest belwo :
AND nvl(sodRow.activity_result_code, 'ASD') = 'ASD'
AND nvl(configRow.activity_result_code, 'ACL') = 'ACL'
or also alternatively :
problem is that it won't return any rows. I.E If we're looking for 'ACL' then the previous check of 'ASD' becomes true and will render the next check useless.
I think I'm just having trouble visualizing how the joins work here
, thanks !
May be you can try left outer join
left outer join hhs_umx_resp_activity sodRow ON sodRow.reg_request_id = approvedRow.reg_request_id
and nvl
AND nvl(sodRow.activity_result_code, 'ASD') = 'ASD'
it will return records with null in sodRow.activity_result_code or 'ASD' in it

PLSQL - Select works but Select within Where Clause returns no data

This is driving me crazy. I want to do simple comparison of a column and a variable but it just doesn't work. The QUERY 1 in following code returns me my value when i do a simple select, but i use the resulting variable in my 2nd query it just doesn't work..
It looks sooooo simple but I've been working on this for hours. The complete sql proc is
The big confusing thing is that if I replace v_bbg_symbol with some hard coded 'Value' (like 'FEDL01') it gives a correct answer for Query 2, but when I use the variable v_bbg_symbol it just doesn't work any more
Declare
v_bbg_symbol VARCHAR2(50);
V_OLD_INS_NAME Varchar2(50);
Begin
--QUERY 1
SELECT BBG_SYMBOL into v_bbg_symbol FROM quotes_external WHERE ID = 1;
--Gives output - 'FEDL01'
DBMS_OUTPUT.PUT_LINE('I got here:'||v_bbg_symbol||' is my value');
-QUERY 2
SELECT NAME INTO V_OLD_INS_NAME FROM INSTRUMENT
JOIN CURVE_INSTRUMENT ON
INSTRUMENT.INSTRUMENT_ID = CURVE_INSTRUMENT.INSTRUMENT_ID
JOIN GENERIC_INSTRUMENT ON
CURVE_INSTRUMENT.GENERIC_INSTRUMENT_ID = GENERIC_INSTRUMENT.GENERIC_INSTRUMENT_ID
WHERE CURVE_INSTRUMENT.CURVE_SNAPSHOT_ID =
(SELECT MAX(CURVE_INSTRUMENT.CURVE_SNAPSHOT_ID) FROM CURVE_INSTRUMENT)
AND GENERIC_INSTRUMENT.INSTRUMENT_NAME = v_bbg_symbol;
--ORACLE ERROR 'No Data Found'
DBMS_OUTPUT.PUT_LINE('I got here:'||V_OLD_INS_NAME||' is the new value');
END;
The first 'SELECT' gives me value which i select INTO a variable 'v_bbg_symbol', but when I use the same variable 'v_bbg_symbol' in my 2nd QUERY it pretends as if there is no value passed and does not return any result. If I give static value of 'v_bbg_symbol' i.e. ('FEDL01' in this case) in my 2nd QUERY, the results come as expected.
Please help..
Here is your query, with table aliases to facilitate following it:
SELECT NAME INTO V_OLD_INS_NAME
FROM INSTRUMENT i JOIN
CURVE_INSTRUMENT ci
ON i.INSTRUMENT_ID = ci.INSTRUMENT_ID JOIN
GENERIC_INSTRUMENT gi
ON ci.GENERIC_INSTRUMENT_ID = gi.GENERIC_INSTRUMENT_ID
WHERE ci.CURVE_SNAPSHOT_ID = (SELECT MAX(ci.CURVE_SNAPSHOT_ID) FROM CURVE_INSTRUMENT ci) and
gi.INSTRUMENT_NAME = v_bbg_symbol;
What this says is that the maximum ci.curve_snapshot_id is not for the instrument that is associated with v_bbg_symbol. I think you want a correlated subquery:
SELECT NAME INTO V_OLD_INS_NAME
FROM INSTRUMENT i JOIN
CURVE_INSTRUMENT ci
ON i.INSTRUMENT_ID = ci.INSTRUMENT_ID JOIN
GENERIC_INSTRUMENT gi
ON ci.GENERIC_INSTRUMENT_ID = gi.GENERIC_INSTRUMENT_ID
WHERE ci.CURVE_SNAPSHOT_ID = (SELECT MAX(ci2.CURVE_SNAPSHOT_ID)
FROM CURVE_INSTRUMENT ci2
WHERE ci2.instrument_id = i.instrument_id
) and
gi.INSTRUMENT_NAME = v_bbg_symbol;

Modify Return Value of SELECT-Statement (TSQL) [Optimizing query]

Problem:
A Database collumn has a Tristate (0,1,2).
Each of the values are used serversidely.
The Clientcode (which cant be changed anymore) is only able to understand '0,1'.
In the Clients view '1' is identic with '2'. So I want to change the SQL Query in the Database to return '1', if the specific value is > 0.
My current Solution is combining 2 Selects (using UNION SELECT) with different WHERE-Clauses and returning '1' or '0' as static values. Now I'm looking for a solution to 'translate' the value within only ONE SELECT statement.
This is my current Solution:
SELECT
dbo.Nachricht.NachrichtID, dbo.Nachricht.Bezeichnung, '1' AS BetrifftKontoeinrichtung,
FROM dbo.Nachricht INNER JOIN dbo.AdditionalData
ON dbo.Nachricht.NachrichtID = dbo.AdditionalData.NachrichtID
WHERE (dbo.Nachricht.NachrichtID in ( 450,439 ))
AND dbo.AdditionalData.BetrifftKontoeinrichtung > 0
UNION SELECT
dbo.Nachricht.NachrichtID, dbo.Nachricht.Bezeichnung, '0' AS BetrifftKontoeinrichtung,
FROM dbo.Nachricht INNER JOIN dbo.AdditionalData
ON dbo.Nachricht.NachrichtID = dbo.AdditionalData.NachrichtID
WHERE (dbo.Nachricht.NachrichtID in ( 450,439 ))
AND dbo.AdditionalData.BetrifftKontoeinrichtung = 0
You can use a case statement, like this:
SELECT
dbo.Nachricht.NachrichtID, dbo.Nachricht.Bezeichnung,
CASE WHEN dbo.AdditionalData.BetrifftKontoeinrichtung = 0
THEN '0' ELSE '1'
END AS BetrifftKontoeinrichtung,
FROM dbo.Nachricht
INNER JOIN dbo.AdditionalData
ON dbo.Nachricht.NachrichtID = dbo.AdditionalData.NachrichtID
WHERE (dbo.Nachricht.NachrichtID in ( 450,439 ))
Looks like you need to use CASE. A decent tutorial here
http://www.databasejournal.com/features/mssql/article.php/3288921/T-SQL-Programming-Part-5---Using-the-CASE-Function.htm
See the worked example
If you just CAST(CAST(val AS BIT) AS INT) you will get integer 0 for 0 and integer 1 for everything else.