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

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.

Related

Set a Variable in a case expression

I am looking to pass declared variables to build my string. I think I want to set my variable via a case expression but I have not done this before. Here is what I have done thus far.
DECLARE #stu_conv AS VARCHAR(5)
-- I think I need a select here.
set #stu_conv = CASE WHEN ITMMASTER.STU_0 ='KG' THEN'2.2'END
SELECT
YPRIMAT.YCROPYR_0
,ITMMASTER.TCLCOD_0
,SPRICLIST.DCGVAL_3
,ITMMASTER.TSICOD_2
,ITMMASTER.ACCCOD_0
,(BASPRI_0*#stu_conv) AS ImportstringAS Importstring
FROM LIVE.YPRIMAT
INNER JOIN LIVE.ITMMASTER ON YPRIMAT.ITMREF_0 = ITMMASTER.ITMREF_0
LEFT OUTER JOIN LIVE.SPRICLIST ON ITMMASTER.TCLCOD_0 = SPRICLIST.PLICRI1_0
WHERE SPRICLIST.PLICRD_0 = 'SPL000020'
I don't see the point for using a variable here, and trying to set it outside the query does not make sense, since you most likely want the value to reset for each row.
I would suggest moving the case expression into the query, as follows:
select
y.ycropyr_0,
i.tclcod_0,
s.dcgval_3,
i.tsicod_2,
i.acccod_0,
baspri_0 * case when i.stu_0 = 'KG' then 2.2 else 1 end as importstringas importstring
from live.yprimat y
inner join live.itmmaster i on y.itmref_0 = i.itmref_0
left outer join live.spriclist s on i.tclcod_0 = s.plicri1_0
where s.plicrd_0 = 'SPL000020'
I assumed that you want a value of 1 when stu_0 is not 'KG', but you can change this as needed.
Side note:
I modified your query to use table aliases. This makes the query shorter to write and somehow easier to read
you would need to prefix column baspri_0 with the table it belongs to (as your query is, it is not possible to tell)
I'm not sure why you're declaring a string and then multiplying it, but I would just inline the case (and add a default case?):
,(BASPRI_0 * CASE
WHEN ITMMASTER.STU_0 ='KG'
THEN 2.2
ELSE ???
END) AS Importstring

how to get a value from the results of another column calculation in same table

I tried to retrieve a value from the calculation of several columns,
in this case try to apply the formula "(a + (a / 25 * b)) - c" to be processed using sql language which I will use in codeigniter.
I also tried using "derived table" like SELECT .... FROM (SELECT... FROM...) AS dt
but I had difficulty when applying it to my case in codeigniter
private function _get_datatables_query(){
$intvl = '2';
$tgl_stok = '2019-09-30';
$this->db->SELECT('p.hso, p.no_part, p.nama_part, jml, sp.oh, sum(p.qty)+(sum(p.qty)/25*$intvl)-sp.oh as suggest');
$this->db->FROM('penjualan p');
$this->db->JOIN('stok_part sp', 'sp.no_part = p.no_part', 'left');
$this->db->WHERE("sp.tgl = '$tgl_stok' AND p.tgl BETWEEN DATE_SUB('$tgl_stok', INTERVAL $intvl DAY) AND '$tgl_stok'");
$this->db->GROUP_BY('p.no_part');
//...other code...
}
I want a column with the alias suggest in the code to produce a calculated value of several other columns
I know writing code that I created is not in accordance with the rules of writing SQL, I tried a number of ways but it did not work. I am very grateful for your help
Instead of var you should use parameter in this way you can easly pass the value you need and avoid sqlinjection eg:
$sql = "SELECT p.hso, p.no_part, p.nama_part, jml, sp.oh, sum(p.qty)+(sum(p.qty)/25*?)-sp.oh as suggest
FROM enjualan p
LEFT JOIN stok_part sp ON sp.no_part = p.no_part
WHERE sp.tgl = ? AND p.tgl BETWEEN DATE_SUB(?, INTERVAL ? DAY) AND ?
GROUP BY p.no_part";
$this->db->query($sql, array($intval,$tgl_stok, $tgl_stok, $intvl, tgl_stok ));
}
I was solved this case.
the problem is in my query:
$this->db->SELECT('p.hso, p.no_part, p.nama_part, jml, sp.oh, sum(p.qty)+(sum(p.qty)/25*$intvl)-sp.oh as suggest');
and replace it with
$this->db->SELECT("p.hso, p.no_part, p.nama_part, sum(p.qty) as jml, sp.oh, sum(p.qty)+(sum(p.qty)/25*'$intvl')-sp.oh as s_po");
my mistake was writing quotation marks in the query

SQL query with "as" keyword

I have a below SQL query running in one of my project. I am struggling to understand the "as" concept here. In the result "user_key" and "user_all" are appearing as empty. Where as at the front end "user_all" is the combination of "rx.ord_by_userid" + "rx.ord_by_inst_id,"
SELECT rx.rx_id,
rx.pt_visit_id,
rx.pt_id,
pt_visit.date_time_sch,
' ' as print_dea_ind,
' ' as phys_rx_label,
rx.ord_by_userid,
rx.ord_by_inst_id,
' ' as user_key,
pt_visit.visit_inst_id,
' ' as user_all,
' ' as tp_agt_ind,
FROM rx LEFT OUTER JOIN tx_pln ON rx.tp_name = tx_pln.tp_name AND rx.tp_vers_no = tx_pln.tp_vers_no, pt_visit
WHERE ( pt_visit.pt_visit_id = rx.pt_visit_id ) and
( pt_visit.pt_id = rx.pt_id ) and
( ( rx.pt_id = :pt_id ) and
( rx.rx_id = :rx_id ) )
Thanks.
I think when they query database, they need two fields called "user_key" and "user_all" with empty value for some purpose. However, in the front end, they need to display column "user_all" with the combination of "rx.ord_by_userid" + "rx.ord_by_inst_id" because of business rule.
The meaning of "AS" is just setting the alias of any field which is needed to have a new name. In this situation, new columns "user_key" and "user_all" are set with empty value.
AS just provides the field in the data set a name, or in SQL terms, an alias. In PB, this is usually done so that the DataWindow gives it a consistent, easy name. That is all that AS does.
The other part of your mystery is how these get populated with non-blank values. You were assuming this was done in the SQL with AS, but we can assure you that is not the case. Most likely, this value is being set in a script that fires in the client after the Retrieve() (if I were to bet, I'd bet a script on the DataWindow control, maybe RetrieveRow or RetrieveEnd).

SQL regex and field

I want to change the query to return multiply values in extra_fields, how can I change the regex? Also I don't understand what extra_fields is - is it a field? If so why it is not called with the table prefix like i.extra_fields?
SELECT i.*,
CASE WHEN i.modified = 0 THEN i.created ELSE i.modified END AS lastChanged,
c.name AS categoryname,
c.id AS categoryid,
c.alias AS categoryalias,
c.params AS categoryparams
FROM #__k2_items AS i
LEFT JOIN #__k2_categories AS c ON c.id = i.catid
WHERE i.published = 1
AND i.access IN(1,1)
AND i.trash = 0
AND c.published = 1
AND c.access IN(1,1)
AND c.trash = 0
AND (i.publish_up = '0000-00-00 00:00:00'
OR i.publish_up <= '2013-06-12 22:45:19'
)
AND (i.publish_down = '0000-00-00 00:00:00'
OR i.publish_down >= '2013-06-12 22:45:19'
)
AND extra_fields REGEXP BINARY '(.*{"id":"2","value":\["[^\"]*1[^\"]*","[^\"]*2[^\"]*","[^\"]*3[^\"]*"\]}.*)'
ORDER BY i.id DESC
The extra_fields is a column of the #__k2_items table. The table qualifier can be omitted, because it is not ambiguous in this query. The column is JSON encoded. That is a serialization format used to store information which is not searchable by design. Applying a RegExp may work one day, but fail another day, since there is no guarantee for id preceeding value (as in your example).
The right way
The right way to filter this is to ignore the extra_fields condition in the SQL query an evaluate in the resultset instead. Example:
$rows = $db->loadObjectList('id');
foreach ($rows as $id => $row) {
$extra_fields = json_decode($row->extra_fields);
if ($extra_fields->id != 2) {
unset($rows[$id]);
}
}
The short way
If you can't change the database layout (which is true for extensions you want to keep updateable), you must split the condition into two, because there is no guarantee for a certain order of the subfields. For some reason, one day value may occur before id. So change your query to
...
AND extra_fields LIKE '%"id":"2"%'
AND extra_fields REGEXP BINARY '"value":\[("[^\"]*[123][^\"]*",?)+\]'
Prepare an intermediate table to hold the contents of extra_fields. Each extra_fields field will be converted into a series of records. Then do a join.
Create a trigger and cronjob to keep the temp table in sync.
Another way is to write UDF in Perl that will decode the field, but AFAIK it is not indexable in mysql.
Using an external search engine is out of scope.
Ok, i didnt want to change the db strucure, i gost some help and changed the regex intoAND extra_fields REGEXP BINARY '(.*{"id":"2","value":\[("[^\"]*[123][^\"]*",?)+\]}.*)'
and i got the right resaults
Thanks

What to do with the error "Ambiguous Columns Defined"?

This is my sql command:
select INCOME_TYPE_ID,
REGION_CODE,
FIN_YEAR_CODE,
PORTION_AMOUNT
from INCOME.INCOME_TYPE,
COMMON.REGION,
INCOME.RECEIVE_DOC_PORTION,
INCOME.ASSESS_ORDER_ITEM,
ACCOUNTING.VOUCHER_ITEM_RECEIVE_DOC,
ACCOUNTING.VOUCHER_ITEM,
ACCOUNTING.VOUCHER,
ACCOUNTING.FIN_YEAR
where INCOME.RECEIVE_DOC_PORTION.ASSESS_ORDER_ITEM_ID = INCOME.ASSESS_ORDER_ITEM.ASSESS_ORDER_ITEM_ID
and INCOME.ASSESS_ORDER_ITEM.INCOME_TYPE_ID=INCOME.INCOME_TYPE.INCOME_TYPE_ID
and INCOME.RECEIVE_DOC_PORTION.RECEIVE_DOC_PORTION_ID = ACCOUNTING.VOUCHER_ITEM_RECEIVE_DOC.RECEIVE_DOC_PORTION_ID
and ACCOUNTING.VOUCHER_ITEM_RECEIVE_DOC.VOUCHER_ITEM_ID = ACCOUNTING.VOUCHER_ITEM.VOUCHER_ITEM_ID
and ACCOUNTING.VOUCHER_ITEM.VOUCHER_ID = ACCOUNTING.VOUCHER.VOUCHER_ID
and ACCOUNTING.VOUCHER.REGION_CODE = COMMON.REGION.REGION_CODE
and ACCOUNTING.VOUCHER.FIN_YEAR_CODE = ACCOUNTING.FIN_YEAR.FIN_YEAR_CODE
and I got this error:
Ambiguous Columns Defined
I'm Using SQL Developer as Oracle client.
Apparently one (or more) column names in your select list exists in more than one table of the FROM list.
You need to prefix every column in the SELECT list with the table it's coming from (it's also a good practice to always do that, regardless of the fact if they are ambigous)
Mention name of table before every column in select query.
Ambiguous column means that you have more than one column with the same name in one of the SELECT statements.
Try this instead, prefgixing all selected columns with their fully qualified names (as you have done elsewhere in your SELECT):
select INCOME.INCOME_TYPE.INCOME_TYPE_ID,
COMMON.REGION.REGION_CODE,
ACCOUNTING.FIN_YEAR.FIN_YEAR_CODE,
ACCOUNTING.VOUCHER_ITEM_RECEIVE_DOC.PORTION_AMOUNT
from INCOME.INCOME_TYPE,
COMMON.REGION,
INCOME.RECEIVE_DOC_PORTION,
INCOME.ASSESS_ORDER_ITEM,
ACCOUNTING.VOUCHER_ITEM_RECEIVE_DOC,
ACCOUNTING.VOUCHER_ITEM,
ACCOUNTING.VOUCHER,
ACCOUNTING.FIN_YEAR
where INCOME.RECEIVE_DOC_PORTION.ASSESS_ORDER_ITEM_ID = INCOME.ASSESS_ORDER_ITEM.ASSESS_ORDER_ITEM_ID
and INCOME.ASSESS_ORDER_ITEM.INCOME_TYPE_ID = INCOME.INCOME_TYPE.INCOME_TYPE_ID
and INCOME.RECEIVE_DOC_PORTION.RECEIVE_DOC_PORTION_ID = ACCOUNTING.VOUCHER_ITEM_RECEIVE_DOC.RECEIVE_DOC_PORTION_ID
and ACCOUNTING.VOUCHER_ITEM_RECEIVE_DOC.VOUCHER_ITEM_ID = ACCOUNTING.VOUCHER_ITEM.VOUCHER_ITEM_ID
and ACCOUNTING.VOUCHER_ITEM.VOUCHER_ID = ACCOUNTING.VOUCHER.VOUCHER_ID
and ACCOUNTING.VOUCHER.REGION_CODE = COMMON.REGION.REGION_CODE
and ACCOUNTING.VOUCHER.FIN_YEAR_CODE = ACCOUNTING.FIN_YEAR.FIN_YEAR_CODE
I had to guess the filly qualified name for
ACCOUNTING.VOUCHER_ITEM_RECEIVE_DOC.PORTION_AMOUNT
It might be
INCOME.RECEIVE_DOC_PORTION.PORTION_AMOUNT
But you should be able to resolve that easily.
Hope it helps...