ABAP 7.40 SELECT .. ENDSELECT UP TO n ROWS syntax? - select

Update: The question should be withdrawn, the grammar is correct. Apparently, SAP defines ABAP via a grammar, which is then modified by additional rules in plain text. I missed this second part.
I'm looking at the ABAP Keyword Documentation 7.40, SELECT -> SELECT additions. For addition UP TO n ROWS, it gives the example
DATA: wa_scustom TYPE scustom.
SELECT *
FROM scustom
WHERE custtype = 'B'
ORDER BY discount DESCENDING
INTO #wa_scustom
UP TO 3 ROWS.
ENDSELECT.
I verified that code in an SAP 7.40 system and got the error
Row 7: "INTO" is not valid here. '.' is expected
On the other hand, the following code is accepted, although it is not covered by the grammar of SELECT as given in the document: UP TO n ROWS should be after the FROM.
SELECT COUNT(*) UP TO 1 ROWS
FROM MARC
WHERE matnr eq 100.
As we are writing a tool that automatically generates ABAP code, it would be nice to know what's legal and what's not. Is there a "definitive" document? In general, is it worth the try to contact someone at SAP for corrections? (You see, I'm somewhat alien to the SAP world) If yes, who could that be?

I can't check it right now, but I suspect that you have "INTO ..." and "UP TO ..." parts placed after "WHERE ..." and "ORDER BY ..." parts.
Documentation states that the syntax of SELECT is:
SELECT result
INTO target
FROM source
[WHERE condition]
[GROUP BY fields]
[HAVING cond]
[ORDER BY fields].
with remark "The FROM clause ... can also be placed before the INTO clause." There are no remarks that you can insert WHERE/GROUP BY/HAVING/ORDER BY between SELECT/INTO/FROM.
So, give a try to:
SELECT *
FROM scustom
INTO #wa_scustom
UP TO 3 ROWS
WHERE custtype = 'B'
ORDER BY discount DESCENDING.
ENDSELECT.

Indeed, there's a syntax imposed by SAP that only allows "UP TO n ROWS" and other arguments to come BEFORE the WHERE clause. However, there are more flexibility to it in newer ABAP server releases.
When generalizing, please use the older syntax. It will still work on newer versions, as SAP has a strong backward compatibility policy.
"Be conservative in what you send, be liberal in what you accept".
Something like that:
SELECT {fields}
FROM {db table}
INTO {work area}
UP TO {n} ROWS
WHERE {field} {condition} {value}
ORDER BY {field} {ASCENDING/DESCENDING}.
ENDSELECT.
Hope it helps.

Related

Sort a group with specified value at the end

I'm trying to order a group in Crystal Reports 2016, with a value that should be shown at the end of the group.
For example, these are the values (strings) from the group:
B
A
C
D
E
I want to put "C" at the end and the rest ascending, like this:
A
B
D
E
C
My SQL expression is already sorting it in the desired order, but that doesn't work.
I tried it with "Specified Order", but the value C would be the first record.
Also the formula on Groupoptions for sorting with checkbox "Use Formula for Groupordering.." doesn't work for me, because i can't even select the databasefield...
(I'm using the german version of crystal reports, the exact description of the elements may be different)
How could i get this to work?
If your query already returns the correct order , just use "in original order." instead of specified

DSUM function in crystal reports?

I am trying to convert an old Microsoft Access report into Crystal reports. I have everything working perfectly except for this last small detail. The Access report uses a DSUM function within an if statement for one of the fields on the report.
After much searching, I've determined that CR doesn't have anything similar.
Here's basically what I'm dealing with.
I have a proposal report. In the details of the report I print the qty, description, and a couple of price fields.
The data looks like something this:
Proposalnum Partitem RolltoItem Unitprice
18611.............1.......... NULL........0.00
18611.............2......... NULL.......17225.92
18611............3............ 2............156.90
18611............4............. 2............482.05
What I need to do is when I print a specific part, I need to query through the rest of the records to find the parts that have a matching number in the rolltoitem field and add the unitprice to the part I'm printing.
So in this example when I print partitem #2, I need to add the 156.90 and the 482.05 from parts 3 and 4 to the 17225.92 so I print a total of 17864.87.
Is there any way to do this?
As far as i know, there is no such function.
But I would try this.
The general idea is: group the data by ProposalNum and use a subreport to select the "children rows" and sum the "children prices".
Details:
Create an empty group section by PartItem.
If you want to show only items where RoolToItem is null, use a suppress function for this case.
In the details section, put a subreport. The data source of the subreport would be the same of the main report.
Change subreport links to select data in subreport based on fields: PartItem in the main report = RolltoItem in the subreport.
Pass other fields to the subreport without select data: ProposalNum, PartItem, UnitPrice. I think you need to create parameters in the subreports before doing that - example: ParentProposalNum, ParentPartItem, ParentUnitPrice.
Create a new formula: ParentUnitPrice + Sum ({YourDataSource.UnitPrice})
Put the formula in the subreport footer a long with the other fields. Maybe: ParentProposalNum, ParentPartItem, formula.
It is a theoretical solution. I hope it points out to the right direction.
If you are trying to sum the Unitprice column for all items that have the same value in Rolltoitem, you could do this with a SQL Expression Field. The code would look something like this. My Where clause may need tweaked though since I'm not sure what your database structure looks like.
(
Select Sum("YourDataBaseTableName"."Unitprice")
From YourDataBaseTableName
Where "YourDataBaseTableName"."Rolltoitem" = *currentRolltoitemValue*
)
Syntax can also vary for SQL Expression Fields based upon what type of database you are using. The syntax I provided is fairly general, but should work on SQL Server.
EDIT: Adding example with explanation of how it works.
Here is one of my SQL Expression Fields from a crystal report that prints a Bill of Lading for shipped goods.
(
Select Sum("SHIPMENTS"."PALLET_COUNT")
From SHIPMENTS
Where "SHIPMENTS"."BOL_ID" = "BOL"."ID"
)
In my database the BOL table is the starting point. A single BOL can contain 1 or more SHIPMENTS, and a single SHIPMENTS can contain one or more PRODUCTS.
Top level grouping is on BOL.ID. The PALLET_COUNT is found once and only once on each SHIPMENTS. I also had a sorting requirement for the data in the details section that prevented me from using a Running Total Field.
This allows a BOL with 2 SHIPMENTS that contains a total of 3 products to look like this:
BOL.ID SHIPMENTS.ID SHIPMENTS.BOL_ID PALLET_COUNT PRODUCT.ID
1 10 1 2 XXX
1 9 1 1 YYY
1 10 1 2 ZZZ
The correct PALLET_COUNT for this BOL should be 3, since PRODUCTS XXX and ZZZ are in the same SHIPMENTS and the PALLET_COUNT is duplicated because of its relationship to the PRODUCTS.

How to use a (repeating) aggregate function value with other columns from the table I use the aggregate function on

Problem: I have to count the number of times a certain user has a certificate and then return the users name, his number of certificates and the difference between the maximum number of certificates across all users and this specific users number of certificates. I succeeded in the first part (getting the number of certificates) which I'll denote as $query$ (because I have a feeling my problem has something to do with aliasing).
So $query$ looks like this:
User |N_Certificates
Geoff 4
Ann 2
Lisa 0
And my end result should look like this:
User |N_Certificates |Difference
Geoff 4 0
Ann 2 2
Lisa 0 4
I tried this query:
SELECT Sub.name, Sub.N_Certificates,
MAX(Sub.N_Certificates)- Sub.Certificates AS Difference FROM ($_query_$) AS SUB
but it returned a error (because I was trying to use an aggregate function in combination with a column I was not grouping by) or a wrong result (notably, difference=0 for all columns).
I tried a contraption with INNER JOIN on another version of sub (same $query$ code with another alias) but it also didn't work (same reason). I could ofcourse hard code the max but I don't think that's a good solution. My about screen tells me I'm using version 1.18 of pg_Admin.
You can't do it in this way, SQL syntax doesn't allow this.
The easiest way is to use a subquery:
SELECT Sub.name, Sub.N_Certificates,
(SELECT MAX(Sub.N_Certificates) FROM ($_query_$))
-
Sub.Certificates AS Difference
FROM ($_query_$) AS SUB
You can also use a common table expression:
WITH some_alias AS(
SELECT * FROM ($_query_$)
)
SELECT name, N_Certificates.
(SELECT MAX(N_Certificates)FROM some_alias)
-
Certificates AS Difference
FROM some_alias
And you can use a windows function: http://www.postgresql.org/docs/9.1/static/tutorial-window.html
SELECT Sub.name, Sub.N_Certificates,
MAX(Sub.N_Certificates) OVER ()
-
Sub.Certificates AS Difference
FROM ($_query_$) AS SUB

n-th row in PostgreSQL for p-quantile

I'm trying to fetch the n-th row of a query result. Further posts suggested the use of OFFSET or LIMIT but those forbid the use of variables (ERROR: argument of OFFSET must not contain variables). Further I read about the usage of cursors but I'm not quite sure how to use them even after reading their PostgreSQL manpage. Any other suggestions or examples for how to use cursors?
My main goal is to calculate the p-quantile of a row and since PostgreSQL doesn't provide this function by default I have to write it on my own.
Cheers
The following returns the 5th row of a result set:
select *
from (
select <column_list>,
row_number() over (order by some_sort_column) as rn
) t
where rn = 5;
You have to include an order by because otherwise the concept of "5th row" doesn't make sense.
You mention "use of variable" so I'm not sure what you are actually trying to achive. But you should be able to supply the value 5 as a variable for this query (or even a sub-select).
You might also want to dig further into windowing functions. Because with that you could e.g. do a sum() over the 3 rows before the current row (or similar constructs) - which could also be useful for you.
if you would like to get 10th record, below query also work fine.
select * from table_name order by sort_column limit 1 offset 9
OFFSET simply skip that many rows before beginning to return rows as mentioned in LIMIT clause.

Conditional Formula Bombs Crystal Report

I have an issue with one of my crystal reports and it's conditional formula. The formula is shown below. I have two tables that hold different customer info that link to my invhdr table. when the account code is cash I need to retrieve a field from one table and visa versa.. The trouble is crystal seems to ignore the conditional formula as written below and seems to require that a link to the field exists even though the formula should prevent the requirement to retrieve the field..
can anyone explain a possible workaround. maybe a conditional table link if it's possible? Thanks ND
if Len("" + {invhdr.ACCT}) > 0 then
if {invhdr.ACCT} = 'CASH' then {CashCust.CUSTOM1}
else if {invhdr.ACCT} <> 'CASH' then {Lookup.VATREGNO}
else "";
so say if invhdr.ACCT ='test' it seems to still need a link to {CashCust.CUSTOM1}
all my tables are linked as inner join , not enforced
I suggest changing the inner joins to left outer joins (from the invhdr table to the CashCust and Lookup tables, so that invhdr is on the inside of the joins). This is unrelated to the formula - if you have inner joined all three tables, then corresponding records must exist on all three tables for rows to be returned; this is the definition of an inner join.
You may then encounter issues with the formula if invhdr.ACCT is null - a quirk of older versions of Crystal was that if any part of a formula evaluated to null, then the whole formula evaluated to null. This behaviour may have been amended in more recent versions of Crystal, but if not then "" + {invhdr.ACCT} may evaluate as null - try checking the value with the IsNull function instead.