Using REF within a FOR expression - unexpected behaviour - loops

I have some example code below:
DATA(t_strings) = VALUE char10_t( ( 'S1' ) ( 'S2' ) ( 'S3' ) ).
TYPES : BEGIN OF gty_ref,
string_ref TYPE REF TO data,
END OF gty_ref,
gty_t_ref TYPE STANDARD TABLE OF gty_ref.
DATA : t_string_ref1 TYPE gty_t_ref,
t_string_ref2 TYPE gty_t_ref.
t_string_ref1 = VALUE #( FOR entry IN t_strings ( string_ref = REF #( entry ) ) ).
LOOP AT t_strings ASSIGNING FIELD-SYMBOL(<entry>).
t_string_ref2 = VALUE #( BASE t_string_ref2 ( string_ref = REF #( <entry> ) ) ).
ENDLOOP.
I thought that using FOR or using a loop would provide the same result. But this is what happens:
Can someone explain why T_STRING_REF1 only contains references to S3 as opposed to S1, S2 and S3 as I expected?

entry in your example is a full-fledged variable. It's not scoped to the FOR-expression. You can still access it afterwards with whatever value it had after the expression.
That means what happens in each loop of your FOR expression is:
The value of the current line of t_string is copied into entry
A new structure is initialized, where string_ref points to entry, not to the line of the table where it got its value from
That structure is put into the resulting table t_string_ref1
So the result is a table of references which all point to the entry variable with whatever value it has at the moment, not into the t_string table.
OK, so how to we solve this problem?
The actual crux here is that a FOR variable IN table expression behaves like LOOP AT table INTO variable. It creates a copy of each line. What you actually need here is to use the equivalent of a LOOP AT table ASSIGNING <field_symbol>. Fortunately, FOR can do that by simply declaring the line-variable in < and >.
t_string_ref1 = VALUE #( FOR <entry> IN t_strings ( string_ref = REF #( <entry> ) ) ).
Now <entry> is no longer a variable but a field-symbol. You can't reference a field symbol. So REF #( <entry> ) gets you a reference to the table line the field-symbol is currently assigned to.

Related

How to get value in loop by determine table field dynamically?

In SAP there is a table T552A. There are several fields like TPR, TTP, FTK, VAR, KNF as per day of a month such as TPR01, TPR02, etc.
In a loop I would like to access the said fields by determining the table field dynamically instead of hard coding of field name, like below:
DATA: ld_begda LIKE sy-datum,
ld_endda LIKE sy-datum.
DATA: lc_day(2) TYPE c.
DATA: lc_field(10) TYPE c.
DATA: lc_value TYPE i.
ld_begda = sy-datum.
ld_endda = ld_begda + 30.
WHILE ld_begda <= ld_endda.
lc_day = ld_begda+6(2).
CONCATENATE 't552a-tpr' lc_day INTO lc_field.
lc_value = &lc_field. " Need support at this point.
ld_begda = ld_begda + 1.
ENDWHILE.
Something like this (depending on the exact requirement):
FIELD-SYMBOLS: <lv_tpr> TYPE tprog.
DATA: ls_t552a TYPE t552a.
DATA: lv_field TYPE fieldname.
WHILE ld_begda <= ld_endda.
lv_field = |TPR| && ld_begda+6(2). "Dynamic field name
ASSIGN COMPONENT lv_field
OF STRUCTURE ls_t552a
TO <lv_tpr>.
IF sy-subrc EQ 0.
... "<lv_tpr> has now the value of the corresponding field
ENDIF.
ld_begda = ld_begda + 1.
ENDWHILE.
To store the result of a dynamic field a variable is needed that can store values of arbitrary types, in ABAP this is supported through field symbols.
A component of a structure (i.e. the row of the table) can then be assigned to a field symbol with ASSIGN COMPONENT:
ASSIGN COMPONENT lc_field OF STRUCTURE row_of_table TO FIELD-SYMBOL(<value>).
" work with <value> here
Recently new generic expressions were introduced (and now also support structures) which would allow you to write this (sooner or later):
... row_of_table-(lc_field) ...

Copying a tables values in Lua is changing the originals value

I want to copy a simple table however whenever I change the copied table it changes the original table too and it's very bizarre
function tablelength(T)
local count = 0
for _ in pairs(T) do count = count + 1 end
return count
end
board = {}
print("board: "..tablelength(board))
newBoard = board
newBoard[tablelength(newBoard) + 1] = 1
print("board: "..tablelength(board))
newBoard[tablelength(newBoard) + 1] = 1
print("board: "..tablelength(board))
Output:
board: 0
board: 1
board: 2
newBoard = board only created a new reference newBoard, second after board, to the same table, which was creared by {}.
You need to deep copy a table, if you want a new one.
Something like this:
local function deep_copy( tbl )
local copy = {}
for key, value in pairs( tbl ) do
if type( value ) ~= 'table' then
copy[key] = value
else
copy[key] = deep_copy( value )
end
end
return copy
end
This is a naïve implementation that does not copy metatables and keys that are tables (it's possible in Lua), and tables that include references to themselves, but it's likely to be enough for your purposes.
Further reading: https://www.lua.org/pil/2.5.html, starting from paragraph two.
P.S. A less naïve implementation, based on http://lua-users.org/wiki/CopyTable:
local function deep_copy( original, copies )
if type( original ) ~= 'table' then return original end
-- original is a table.
copies = copies or {} -- this is a cache of already copied tables.
-- This table has been copied previously.
if copies[original] then return copies[original] end
-- We need to deep copy the table not deep copied previously.
local copy = {}
copies[original] = copy -- store a reference to copied table in the cache.
for key, value in pairs( original ) do
local dc_key, dc_value = deep_copy( key, copies ), deep_copy( value, copies )
copy[dc_key] = dc_value
end
setmetatable(copy, deep_copy( getmetatable( original ), copies) )
return copy
end

Postgresql: Append object to a list

I have data that has a key as string and value is a list of json, looks like
ID|data
A1|{key1:[{k1:v1,k2:v2},{{k3:v3,k4:v4}]}
I want to append json, say, {k9:v9,k7:v6} to this list for the key1, something like-
ID|data
A1|{key1:[{k1:v1,k2:v2},{{k3:v3,k4:v4},{k9:v9,k7:v6}]}
I have tried jsonb_set and other functions but they were of no use, example-
UPDATE tbl_name
SET data = jsonb_set(data,'{key1,1}','{k9:v9,k7:v6}'::jsonb) where ID = 'A1'
You need to use jsonb_insert() function in order to append that part, after fixing the format of JSONB value ( otherwise you'd get "ERROR: invalid input syntax for type json" ) :
UPDATE tbl_name
SET data = jsonb_insert(data,'{key1,1}','{"k9":"v9","k7":"v6"}'::jsonb)
WHERE ID = 'A1'
Demo

How to write MDX Query for getting grouped results

I have Cube where data is aggregated and all I need to have count of records against each 2 digit zip code.
Attached image shows my cube hierarchies and measures.
I applied query like :
WITH MEMBER [Measures].NoOfConsignments as Count(([Consignment].[Target Address Name].[Target Address Name]))
select filter([Consignment].[Distribution Area].[Zip2], [Consignment].[Distribution Area].[Target Address Country] = "94") on rows,
{[Measures].NoOfConsignments} on columns
from [RebellOlap]
where ({[Consignment].[Distribution Area].[Target Address Country].&[94]})
but is throwing an error :
"The Distribution Area hierarchy already appears in the Axis1 axis"
I re-structured and formulated following sub-select query in the following way :
WITH MEMBER [Measures].NoOfConsignments as Count(([Consignment].[Target Address Name].[Target Address Name]))
Select
NON EMPTY [Measures].NoOfConsignments on columns,
NON EMPTY [Consignment].[Distribution Area].[Zip2] on rows
FROM (
SELECT {[Consignment].[Distribution Area].[Zip2],[Consignment].[Distribution Area].[Target Address Country].&[94]}
FROM [RebellOlap]
)
but it also returned me the 'ambiguity error'.
all I need Output in following manner
Edit
AllConsignments in Germany
All Consignments in Germany against specific Zip Code
I just applied filter for all zip codes and introduce "Range" as Operator with 'Filter Expression' and it worked!!
SELECT NON EMPTY { [Measures].[ConsignmentCount] } ON COLUMNS,
NON EMPTY { ([Consignment].[Distribution Area].[Zip2].ALLMEMBERS ) } ON ROWS
FROM ( SELECT ( [Consignment].[Distribution Area].[Zip2].&[94]&[0]&[01] : [Consignment].[Distribution Area].[Zip2].&[94]&[9]&[99] ) ON COLUMNS
FROM [RebellOlap])
Based on your edits, I feel below should work out for you:
WITH MEMBER Measures.NumConsignment as
COUNT
(
NonEmpty
(
[Consignment].[Target Address Name].[Target Address Name].MEMBERS,
([Consignment].[Distribution Area].CURRENTMEMBER, Measures.[Num. Consignments])
)
)
SELECT [Consignment].[Distribution Area].[Zip2].MEMBERS on 1,
Measures.NumConsignment ON 0
FROM
(
SELECT [Consignment].[Distribution Area].[Target Address Country].&[94] ON 0
FROM [RebellOlap]
)
While the rest of the code is pretty standard, there is one part which might need explanation.
NonEmpty
(
[Consignment].[Target Address Name].[Target Address Name].MEMBERS,
([Consignment].[Distribution Area].CURRENTMEMBER, Measures.[Num. Consignments])
)
This is returning a set of those Target addresses for the current zip for whom the num of consignments is not empty( i.e. <>0)

Arithmetic overflow error converting nvarchar to data type numeric

I have a stored procedure which works fine in my local environment as well as the QA environment.
However at the UAT environment at the client it gives the error
System.Data.SqlClient.SqlException:
Message number="8115" severity="16" state="8">Arithmetic overflow error
converting nvarchar to data type numeric.
It also works fine in one of the local instances installed in the client location.
I found out the line of code which gives an error by commenting/umcommenting lines of code and zeroed it down to
(
#TotalHHInternalTo IS NULL
OR
(
IsNumeric(E.[Xml].value(
'declare default element namespace "http://www.xyz/1.0";
(/Event/Data/CustomData/DataXML/ProductData/ProductParty/ProductCategory[#code != ''Protection'']/Product/FundValue)[1]',
'nvarchar(50)'
) ) = 1
AND
EXISTS
(
SELECT
1
FROM E.[Xml].nodes(
'declare default element namespace "http://www.xyz/1.0";
/Event/Data/CustomData/DataXML/ProductData/ProductParty/ProductCategory[#code != ''Protection'']/Product') as P(E)
HAVING SUM(P.E.value(
'declare default element namespace "http://www.xyz/1.0";
(FundValue)[1]',
'decimal'
)) <= #TotalHHInternalTo
)
)
)
The variable #TotalHHInternalTo is a parameter of type decimal which is part of search criteria passed to the stored procedure as an xml. Under Product party I have 4 product category and I need to total up all Fund value except of type Protection. If the sum of these 3 types is less than #TotalHHInternalTo I would like to display it in the search result.
I added the isNumeric condition to check if the value picked up from the xml is a numeric value.
However I still get an overflow error.
Check my brackets, but you need to convert it to explicit short-circuit boolean evaluation using CASE. SQL Server is free to evaluate AND conditions in either order, and you got the wrong end of the stick.
CASE WHEN IsNumeric(E.[Xml].value(
'declare default element namespace "http://www.focus-solutions.co.uk/focus360/1.0";
(/Event/Data/CustomData/DataXML/ProductData/ProductParty/ProductCategory[#code != ''Protection'']/Product/FundValue)[1]',
'nvarchar(50)'
) ) = 0
THEN 0
WHEN
EXISTS
(
SELECT
1
FROM E.[Xml].nodes(
'declare default element namespace "http://www.focus-solutions.co.uk/focus360/1.0";
/Event/Data/CustomData/DataXML/ProductData/ProductParty/ProductCategory[#code != ''Protection'']/Product') as P(E)
HAVING SUM(P.E.value(
'declare default element namespace "http://www.focus-solutions.co.uk/focus360/1.0";
(FundValue)[1]',
'decimal'
)) <= #TotalHHInternalTo
)
THEN 1
ELSE 0 END = 1

Resources