How to join the tables KNBK and TIBAN? - abap

I am trying to read the table TIBAN only in the cases when the field BANKN of the KNBK table contains the string "IBAN". The problem that I am having is reading the table TIBAN. For the KNBK table I used the key kunnr to search the table, but the table TIBAN does not contain this field. The code is as follows:
LOOP AT lt_data_bsec ASSIGNING <ls_data_bsec>.
READ TABLE lt_data_knbk ASSIGNING FIELD-SYMBOL(<ls_data_knbk>)
WITH KEY kunnr = <ls_data_bsec>-kunnr BINARY SEARCH .
IF <ls_data_knbk>-bankn_kn CS '<IBAN>'.
>>>>> READ TABLE lt_data_tiban ASSIGNING FIELD-SYMBOL(<ls_data_tiban>).
IF <ls_data_bsec> IS ASSIGNED
AND <ls_data_tiban> IS ASSIGNED
AND ( <ls_data_bsec>-banks_bs NE <ls_data_tiban>-banks_kn
OR <ls_data_bsec>-bankl_bs NE <ls_data_tiban>-bankl_kn
OR <ls_data_bsec>-bankn_bs NE <ls_data_tiban>-bankn_kn ).
ENDIF.
The declaration of the lt_data_knbk table is:
SELECT kunnr,
banks AS banks_kn,
bankl AS bankl_kn,
bankn AS bankn_kn
FROM knbk
INTO TABLE #DATA(lt_data_knbk)
FOR ALL ENTRIES IN #lt_data_bsec
WHERE kunnr = #lt_data_bsec-kunnr
ORDER BY PRIMARY KEY.
And the declaration of the lt_data_tiban is:
SELECT banks AS banks_tb,
bankl AS bankl_tb,
bankn AS bankn_tb
FROM tiban
INTO TABLE #DATA(lt_data_tiban).
Also the declaration of the lt_data_bsec is of the type gty_out:
BEGIN OF gty_out,
bukrs TYPE bukrs,
belnr TYPE belnr_d,
buzei TYPE buzei,
budat TYPE budat,
kunnr TYPE kunnr,
banks_kn TYPE banks,
bankl_kn TYPE bankk,
bankn_kn TYPE bankn,
banks_bs TYPE banks,
bankl_bs TYPE bankk,
bankn_bs TYPE bankn,
banks_tb TYPE banks,
bankl_tb TYPE bankk,
bankn_tb TYPE bankn,
END OF gty_out,
I am having an error in the line that I have marked in the code as it needs a key field to do the reading of the table.
May anyone know what type of field do I need to do a search of the TIBAN table, just like I did for the KNBK table?

I would recommend you to acquire your bank data by doing a LEFT OUTER JOIN with TIBAN.
SELECT knbk~kunnr AS kunnr,
knbk~banks AS banks_kn,
knbk~bankl AS bankl_kn,
knbk~bankn AS bankn_kn,
knbk~bkont AS bkont_kn,
tiban~iban AS iban
FROM knbk
LEFT OUTER JOIN tiban ON
tiban~banks = knbk~banks AND
tiban~bankl = knbk~bankl AND
tiban~bankn = knbk~bankn AND
tiban~bkont = knbk~bkont
INTO TABLE #DATA(lt_data_knbk)
FOR ALL ENTRIES IN #lt_data_bsec
WHERE knbk~kunnr = #lt_data_bsec-kunnr
ORDER BY knbk~banks knbk~bankl knbk~bankn knbk~bkont.
The result will be a table with all entries from KNBK, plus a field IBAN which will be filled from TIBAN if a corresponding row exists and be initial when no corresponding row exists.
Now you can just determine whether or not you have an IBAN like this:
LOOP AT lt_data_knbk ASSIGNING FIELD-SYMBOL(<ls_data_knbk>).
IF <ls_data_knbk>-iban IS INITIAL.
" classic bank number + account number account
ELSE.
" IBAN account
ENDIF.
ENDLOOP.

Your task is a perfect case where ABAP CDS associations aka Lazy Join come into play.
If you are on the recent version of ABAP and is able to create CDS, nothing prevents you from creating this
#AbapCatalog.sqlViewName: ‘ZCUST_TIBAN_SQL’
#AbapCatalog.compiler.compareFilter: true
#AccessControl.authorizationCheck: #CHECK
#EndUserText.label: ‘TIBAN’
define view ZCUST_TIBAN as select from knbk as cus
association [0..1] to tiban as _an
on cus.kunnr = _an.kunnr
and cus.banks = _an.banks
and cus.bankl = _an.bankl
and cus.bankn = _an.bankn
and cus.bkont = _an.bkont
{
cus.kunnr,
cus.banks AS banks_kn,
cus.bankl AS bankl_kn,
cus.bankn AS bankn_kn,
cus.bkont AS bkont_kn,
_iban~
} WHERE cus.bankn like '%IBAN%'
Then you can use this association in all your further requirements:
SELECT banks_kn, bankl_kn, bankn_kn, bkont_kn
\_iban-iban AS IBAN,
\_iban-valid_from AS IBAN_validity
FROM zcust_tiban
WHERE kunnr IN #lr_customers
INTO TABLE #DATA(lt_data_knbk).
...
READ TABLE lt_data_knbk ASSIGNING FIELD-SYMBOL(<ls_data_knbk>)
WITH KEY kunnr = <ls_data_bsec>-kunnr BINARY SEARCH.
...
Key points to pay attention to:
an association with 0..1 cardinality like above does not do actual JOIN until the fields from the associated table TIBAN are requested like in the above SELECT. This can be more performant and more universal than LEFT OUTER JOIN approach suggested by Philipp.
it is reusable and can be used in further selects/associations as a source

Related

DBSQL_SQL_INTERNAL_DB_ERROR SQL error 2048

I have to join two tabled ACDOCA and BKPF. I have written the follow code for it.
SELECT a~rbukrs,
a~racct,
a~bldat,
a~blart,
a~kunnr,
a~belnr,
a~sgtxt,
b~xblnr,
a~budat,
a~hsl,
a~prctr
INTO TABLE #it_final
FROM acdoca AS a
LEFT OUTER JOIN bkpf AS b
ON a~rbukrs = b~bukrs
AND a~gjahr = b~gjahr
WHERE a~rbukrs IN #s_bukrs
AND a~Kunnr IN #s_kunnr
AND a~Budat IN #s_budat
AND a~Belnr IN #s_belnr
AND a~rldnr IN #s_rldnr
AND a~blart = 'DR' OR a~blart = 'ZK' OR a~blart = 'UE'.
Facing the following errors:----
Runtime error: DBSQL_SQL_INTERNAL_DB_ERROR
SQL error "SQL code: 2048" occurred while accessing table "ACDOCA".
Short Text: An exception has occurred in class "CX_SY_OPEN_SQL_DB"
How do I resolve this? please help.
A few things:
Selecting directly from the database tables is error prone (e.g. you'll forget keys while joining) and you have to deal with those terrible german abbreviations (e.g. Belegnummer -> belnr). Since quite some time there are CDS Views on top such as I_JournalEntryItem with associations and proper english names for those fields, if you can use them, I would (also they're C1 released).
As already pointed out by xQBert the query does probably not work as intended as AND has prescendence over OR, and as such your query basically returns everything from ACDOCA, multiplied by everything from BKPF which likely leads to the database error you've posted
With range queries you might still get a lot of results (like billions of entries, depending on your company's size), you should either limit the query with UP TO, implement some pagination or COUNT(*) first and show an error to the user if the result set is too large.
I would write that like this:
TYPES:
BEGIN OF t_filters,
company_codes TYPE RANGE OF bukrs,
customers TYPE RANGE OF kunnr,
document_dates TYPE RANGE OF budat,
accounting_documents TYPE RANGE OF fis_belnr,
ledgers TYPE RANGE OF rldnr,
END OF t_filters.
DATA(filters) = VALUE t_filters(
" filter here
).
SELECT FROM I_JournalEntryItem
FIELDS
CompanyCode,
GLAccount,
DocumentDate,
AccountingDocumentType,
Customer,
AccountingDocument,
DocumentItemText,
\_JournalEntry-DocumentReferenceID,
PostingDate,
AmountInCompanyCodeCurrency,
ProfitCenter
WHERE
CompanyCode IN #filters-company_codes AND
Customer IN #filters-customers AND
DocumentDate IN #filters-document_dates AND
AccountingDocument IN #filters-accounting_documents AND
Ledger IN #filters-ledgers AND
AccountingDocumentType IN ( 'DR', 'ZK', 'UE' )
INTO TABLE #DATA(sales_orders)
UP TO 100 ROWS.
(As a bonus you'll get proper DCL authorization checks)
2048 is/can be a memory allocation error: Too much data being returned. Given that, this line is highly suspect
AND a~blart = 'DR' OR a~blart = 'ZK' OR a~blart = 'UE'.
I'd consider this instead. Otherwise ALL blart ZK and UE records are returned regardless of customer, year, company et...
SELECT a~rbukrs,
a~racct,
a~bldat,
a~blart,
a~kunnr,
a~belnr,
a~sgtxt,
b~xblnr,
a~budat,
a~hsl,
a~prctr
INTO TABLE #it_final
FROM acdoca AS a
LEFT OUTER JOIN bkpf AS b
ON a~rbukrs = b~bukrs
AND a~gjahr = b~gjahr
WHERE a~rbukrs IN #s_bukrs
AND a~Kunnr IN #s_kunnr
AND a~Budat IN #s_budat
AND a~Belnr IN #s_belnr
AND a~rldnr IN #s_rldnr
AND a~blart IN ('DR','ZK','UE').
However, if you really did mean to return all blart ZK, UE records and only those that ar DR and in the defined parameters... you're simply asking for too much data from teh system and need to "LIMIT" your result set and somehow let the user know only a limited set is being returned due to data volume
I'd also ensure your join on keys is sufficient. Fiscal Year and company code represent an incomplete key to BKPF. I dont' know ACDOCA data table so I'm unsure if that's a proper join which may be leading to a semi-cartesean contributing to data bloat. I'd think in a multi-tenant db, you may need to join on mandt as well... possibly a doc number and some other values... again, this lookst to be an incomplete join on key.... so perhaps more is needed there as well.

Should I use an SQL full outer join for this?

Consider the following tables:
Table A:
DOC_NUM
DOC_TYPE
RELATED_DOC_NUM
NEXT_STATUS
...
Table B:
DOC_NUM
DOC_TYPE
RELATED_DOC_NUM
NEXT_STATUS
...
The DOC_TYPE and NEXT_STATUS columns have different meanings between the two tables, although a NEXT_STATUS = 999 means "closed" in both. Also, under certain conditions, there will be a record in each table, with a reference to a corresponding entry in the other table (i.e. the RELATED_DOC_NUM columns).
I am trying to create a query that will get data from both tables that meet the following conditions:
A.RELATED_DOC_NUM = B.DOC_NUM
A.DOC_TYPE = "ST"
B.DOC_TYPE = "OT"
A.NEXT_STATUS < 999 OR B.NEXT_STATUS < 999
A.DOC_TYPE = "ST" represents a transfer order to transfer inventory from one plant to another. B.DOC_TYPE = "OT" represents a corresponding receipt of the transferred inventory at the receiving plant.
We want to get records from either table where there is an ST/OT pair where either or both entries are not closed (i.e. NEXT_STATUS < 999).
I am assuming that I need to use a FULL OUTER join to accomplish this. If this is the wrong assumption, please let me know what I should be doing instead.
UPDATE (11/30/2021):
I believe that #Caius Jard is correct in that this does not need to be an outer join. There should always be an ST/OT pair.
With that I have written my query as follows:
SELECT <columns>
FROM A LEFT JOIN B
ON
A.RELATED_DOC_NUM = B.DOC_NUM
WHERE
A.DOC_TYPE IN ('ST') AND
B.DOC_TYPE IN ('OT') AND
(A.NEXT_STATUS < 999 OR B.NEXT_STATUS < 999)
Does this make sense?
UPDATE 2 (11/30/2021):
The reality is that these are DB2 database tables being used by the JD Edwards ERP application. The only way I know of to see the table definitions is by using the web site http://www.jdetables.com/, entering the table ID and hitting return to run the search. It comes back with a ton of information about the table and its columns.
Table A is really F4211 and table B is really F4311.
Right now, I've simplified the query to keep it simple and keep variables to a minimum. This is what I have currently:
SELECT CAST(F4211.SDDOCO AS VARCHAR(8)) AS SO_NUM,
F4211.SDRORN AS RELATED_PO,
F4211.SDDCTO AS SO_DOC_TYPE,
F4211.SDNXTR AS SO_NEXT_STATUS,
CAST(F4311.PDDOCO AS VARCHAR(8)) AS PO_NUM,
F4311.PDRORN AS RELATED_SO,
F4311.PDDCTO AS PO_DOC_TYPE,
F4311.PDNXTR AS PO_NEXT_STATUS
FROM PROD2DTA.F4211 AS F4211
INNER JOIN PROD2DTA.F4311 AS F4311
ON F4211.SDRORN = CAST(F4311.PDDOCO AS VARCHAR(8))
WHERE F4211.SDDCTO IN ( 'ST' )
AND F4311.PDDCTO IN ( 'OT' )
The other part of the story is that I'm using a reporting package that allows you to define "virtual" views of the data. Virtual views allow the report developer to specify the SQL to use. This is the application where I am using the SQL. When I set up the SQL, there is a validation step that must be performed. It will return a limited set of results if the SQL is validated.
When I enter the query above and validate it, it says that there are no results, which makes no sense. I'm guessing the data casting is causing the issue, but not sure.
UPDATE 3 (11/30/2021):
One more twist to the story. The related doc number is not only defined as a string value, but it contains leading zeros. This is true in both tables. The main doc number (in both tables) is defined as a numeric value and therefore has no leading zeros. I have no idea why those who developed JDE would have done this, but that is what is there.
So, there are matching records between the two tables that meet the criteria, but I think I'm getting no results because when I convert the numeric to a string, it does not match, because one value is, say "12345", while the other is "00012345".
Can I pad the numeric -> string value with zeros before doing the equals check?
UPDATE 4 (12/2/2021):
Was able to finally get the query to work by converting the numeric doc num to a left zero padded string.
SELECT <columns>
FROM PROD2DTA.F4211 AS F4211
INNER JOIN PROD2DTA.F4311 AS F4311
ON F4211.SDRORN = RIGHT(CONCAT('00000000', CAST(F4311.PDDOCO AS VARCHAR(8))), 8)
WHERE F4211.SDDCTO IN ( 'ST' )
AND F4311.PDDCTO IN ( 'OT' )
AND ( F4211.SDNXTR < 999
OR F4311.PDNXTR < 999 )
You should write your query as follows:
SELECT <columns>
FROM A INNER JOIN B
ON
A.RELATED_DOC_NUM = B.DOC_NUM
WHERE
A.DOC_TYPE IN ('ST') AND
B.DOC_TYPE IN ('OT') AND
(A.NEXT_STATUS < 999 OR B.NEXT_STATUS < 999)
LEFT join is a type of OUTER join; LEFT JOIN is typically a contraction of LEFT OUTER JOIN). OUTER means "one side might have nulls in every column because there was no match". Most critically, the code as posted in the question (with a LEFT JOIN, but then has WHERE some_column_from_the_right_table = some_value) runs as an INNER join, because any NULLs inserted by the LEFT OUTER process, are then quashed by the WHERE clause
See Update 4 for details of how I resolved the "data conversion or mapping" error.

How to fix data type conversion SQL error in ABAP when using VBRK and VBRP

I want to get the sum of quantities billed in a fixed period and a defined organisation but It either prints 0 or I get an SQL error as in the screenshot.
I used tables VBRK for Organisation and data and VBRP for quantity and currency.
Tables: VBRK,
lips,
VBRP.
TYPES: Begin of itab5,
VKORG TYPE VBRK-VKORG,
FKDAT TYPE VBRK-FKDAT,
ARKTX TYPE LIPS-ARKTX,
FKIMG TYPE VBRP-FKIMG,
VRKME TYPE VBRP-VRKME,
End of itab5.
DATA: wa_ma5 TYPE itab5,
it_ma5 TYPE STANDARD TABLE OF itab5,
quan TYPE VBRP-FKIMG,
curr TYPE vbrp-vrkme.
SELECT-OPTIONS: DATE FOR VBRK-FKDAT.
SELECT-OPTIONS: Organ FOR VBRK-VKORG.
APPEND date.
APPEND Organ.
START-OF-SELECTION.
SELECT VKORG FKDAT FKIMG VRKME FROM VBRK
inner JOIN VBRP ON VBRP~netwr = VBRK~WAERK
INTO CORRESPONDING FIELDS OF TABLE it_ma5
WHERE VBRK~FKDAT IN DATE AND VBRK~VKORG IN Organ.
END-OF-SELECTION.
quan = 0.
LOOP AT it_ma5 INTO wa_ma5.
* if sy-subrc = 0.
quan = quan + wa_ma5-fkimg.
* endif.
ENDLOOP.
WRITE: 'the quantity', quan.
This is not that complicated.
You are doing join between the two tables using VBRP-NETWR and VBRK-WAERK which doesn't make sense (and they are different data types).
VBRK is linked with VBRP by VBELN field (one is billing header and one billing items), so the correct JOIN is:
START-OF-SELECTION.
SELECT vkorg fkdat fkimg vrkme
FROM vbrk
INNER JOIN vbrp
ON vbrp~vbeln = vbrk~vbeln
INTO CORRESPONDING FIELDS OF TABLE it_ma5
WHERE vbrk~fkdat IN date
AND vbrk~vkorg IN organ.

SAP query. Retrieve values basing on input on selection screen

I am stuck with coding a SAP query..I am new to ABAP.
What I would like to achieve is a join between tables ESLL, EKPO, EKKO.
Specifically these are the steps I would like to achieve:
in the selection parameter every time I will enter the query I will
give a different value for ESLL-EXTSRVNO;
basing on that value the query automatically should select ESLL-PACKNO basing on ESLL-EXTSRVNO given;
then the query should put ESLL-SUB_PACKNO equal
to the ESLL-PACKNO values of the steps before;
then the query should
put the new ESLL-PACKNO values equal to EKPO-PACKNO and retrieve the
following fields: EKPO-EBELN, EKPO-EBELP, EKPO-MATKL.
I have already written some code inside the infoset, but I do not know how to fix it.
In the "data" section I have written:
DATA: it_esll TYPE TABLE OF esll.
DATA: it_esll2 TYPE TABLE OF esll.
DATA: it_ekpo TYPE TABLE OF ekpo.
In the "start-of-selection" section I have written:
SELECT packno
FROM esll
INTO TABLE it_esll.
IF sy-subrc EQ 0.
SELECT packno FROM esll
into TABLE it_esll2
for ALL ENTRIES IN it_esll
where sub_packno EQ it_esll-packno.
IF sy-subrc EQ 0.
SELECT ebeln ebelp bukrs werks matkl menge netpr peinh
FROM ekpo
into TABLE it_ekpo
for ALL ENTRIES IN it_esll2
WHERE packno EQ it_esll2-packno.
endif.
endif.
And, in order to display all the information I want, I have put the following joins:
ESLL-PACKNO --> EKPO-PACKNO --> EKPO-EBELN --> EKKO-EBELN
At then end I would like to display these information:
EKPO-EBELN
EKPO-EBELP
EKPO-MATKL
EKKO-BSART
EKPO-PACKNO
Could you please help me?
One option could be to use Alias table in your infoset, something like this:
First table: ESLL;
Second table ZESLL (Alias on ESLL) with join ZESLL-PACKNO = ESLL-SUB_PACKNO;
Third table: EKPO with join on EKPO-PACKNO = ZESLL-PACKNO;
Fourth table: EKKO with join on EBELN;
So you can avoid ABAP
Infoset Join

When same BELNR value, report only gets info from the first line

I have a report that is getting info from bsis, bsas, bsid, bsad, vbrk, bkpf and bset. the problem is when there are two invoices with the same number, the report copies the info from the first line to the second. So, if invoice no.4100111596 has a tax total of 2.140,20 in the first line, the same invoice number has the same tax toal in the second, but the value is 40.140,64.
What can I do? do I have to use the buzei field some where?
Heres de code:
SELECT SINGLE kbetr fwste hwste
FROM bset
INTO (<fs_main>-kbetr, <fs_main>-fwste, <fs_main>-hwste)
WHERE belnr = <fs_main>-belnr
AND bukrs IN bukrs
AND gjahr IN gjahr.
I tried getting the buzei and it still gives me the same line twice.
Here's the code, that I do before the code above:
SELECT c~kunnr a~belnr d~spart c~bldat c~waers c~wrbtr a~hwaer c~dmbtr c~buzei
INTO CORRESPONDING FIELDS OF TABLE lt_data
FROM ( ( ( bsis AS f
INNER JOIN bkpf AS a ON f~belnr = a~belnr )
INNER JOIN bsid AS c ON c~belnr = a~belnr )
INNER JOIN vbrk AS d ON d~vbeln = c~belnr )
WHERE a~gjahr IN gjahr
AND a~bukrs IN bukrs
AND a~blart = 'R1'
AND f~hkont = '0034930020'
AND ( c~mwskz = 'L0' OR c~mwskz = 'L1' OR c~mwskz = 'L2' ).
-You have to use all key fields on your condition part of your "SELECT" in order to get a "SINGLE" line. Otherwise table might not return you a single line but you'll get the first line.
-So you have to use BUZEI field too on your condition in your select.
Hope it was helpful
Talha
Found the problem. I was doing the selection os the wrong fields i the wrong table. BKPF only gave me one line and BSET gave me two lines. So, I change the tables that was getting the fields from and it work just fine. Thank you.