My internal table looks like this:
Id vendor doc_no debit credit
Abc. Ven1. 123. 4000. 5000
Abc. Ven2. 345. 5000 6000
Abc. Ven1. 367. 8000. 9000
Abc. Ven2. 890. 3000. 8000
Now, I need to find the largest debit for each vendor. On the basis of above example, for vendor Ven1. largest debit will be (4000+8000) = 12000. Similarly for Ven2. vendor the largest debit value will be (5000+3000) = 8000.
How to achieve this??
Here is an example report containing the most performant solution I can think of. It's using the fairly new ABAP commands FOR GROUPS … IN and REDUCE. Just copy it in your system and give it a try.
REPORT Z_SUM_BY_VENDOR.
"Prepare test data
TYPES: BEGIN OF lty_st_data_source,
id TYPE C LENGTH 3,
vendor TYPE C LENGTH 4,
doc_no TYPE C LENGTH 3,
debit TYPE I,
credit TYPE I,
END OF lty_st_data_source.
DATA: lt_data_source TYPE STANDARD TABLE OF lty_st_data_source.
APPEND VALUE #( id = 'Abc' vendor = 'Ven1' doc_no = '123' debit = 4000 credit = 5000 ) TO lt_data_source.
APPEND VALUE #( id = 'Abc' vendor = 'Ven2' doc_no = '345' debit = 5000 credit = 6000 ) TO lt_data_source.
APPEND VALUE #( id = 'Abc' vendor = 'Ven1' doc_no = '367' debit = 8000 credit = 9000 ) TO lt_data_source.
APPEND VALUE #( id = 'Abc' vendor = 'Ven2' doc_no = '890' debit = 3000 credit = 8000 ) TO lt_data_source.
" Define type for sum table and generate sum (actual answer to the question)
TYPES: BEGIN OF lty_st_grouped,
vendor TYPE C LENGTH 4,
debit_sum TYPE I,
END OF lty_st_grouped,
lty_tt_grouped TYPE HASHED TABLE OF lty_st_grouped WITH UNIQUE KEY vendor.
DATA(lt_grouped) = VALUE lty_tt_grouped(
FOR GROUPS vendor_group OF <ls_data_source> IN lt_data_Source
GROUP BY <ls_data_source>-vendor
( VALUE lty_st_grouped( vendor = vendor_group
debit_sum = REDUCE i( INIT sum = 0
FOR <ls_data_source_2> IN GROUP vendor_group
NEXT sum = sum + <ls_data_source_2>-debit )
)
)
).
" Show result for demo purpose
LOOP AT lt_grouped ASSIGNING FIELD-SYMBOL(<ls_grouped>).
WRITE: `Vendor: ` && <ls_grouped>-vendor && ` | Debit Sum: ` && <ls_grouped>-debit_sum.
NEW-LINE.
ENDLOOP.
Create a structure with your required fields in the correct order of dimension(s) and measures. Then you can savely use the "AT" functions, aggregating your debit for each "group of" vendors.
DATA: Begin of ls_itab_ordered
vendor type vendor,
debit type debit,
End of ls_itab_ordered,
lt_itab_ordered like standard table of ls_itab_ordered,
lt_itab_agg like itab.
move-corresponding itab to lt_itab_ordered
sort lt_itab_ordered by vendor.
loop at lt_itab_ordered assigning <ls_itab_ordered>.
at new vendor.
ls_itab_agg-vendor = <ls_itab_ordered>-vendor.
endat.
add <ls_itab_ordered>-debit to ls_itab_agg-debit.
at end of vendor.
append ls_itab_agg to lt_itab_agg.
clear ls_itab_agg.
endat.
endloop.
You can use Collect -
I'm considering it_vendor is your internal table and declare ls_vendor like it_vendor.
TYPES: BEGIN OF ty_calc,
vendor TYPE vendor, " (check DE of vendor then use that)
debit TYPE int4,
END OF ty_calc.
DATA: ls_calc TYPE ty_calc,
lt_calc TYPE TABLE OF ty_calc.
LOOP AT it_vendor INTO ls_vendor.
ls_calc-vendor = ls_vendor-vendor.
ls_calc-debit = ls_vendor-debit.
COLLECT ls_calc INTO lt_calc.
CLEAR : ls_calc , ls_vendor.
ENDLOOP.
Internal table lt_calc holds the largest debit for each vendor.
Related
I have a query that is within a View so I can not use order by . I have a column called Course that column is an Int Column and it can have many repeating values . There is also a column called status that status column is also an Int and it will have unique values . I am trying to create a query where we can get the last row of a particular Course and see if the Status is 2 if yes then add 1 to #MyValue if not then add 0 to the variable .I have an ID column which is the primary key, without order by I am having a tough time figuring this one out .
MyValue is a smallint and we simply assign the value
CourseNumber is a Int and a table can have many (Course) #CourseNumber with the same number
There will only be 1 combination of CourseNumber and Status . We can have many CourseNumbers with the same number etc 120 however each one will have a different Status
What I want to do with the query below is get the last record of #CourseNumber we have an ID column and check if the status of the last record of #CourseNumber is equal to 2 . If it's equal to 2 then assign the value 1 to #MyValue and if it is not equal to 2 then assign the value 0 .
SELECT #MyValue =(SELECT count(*) FROM MyTasks as MT WHERE MT.Course = #CourseNumber And MT.status=2)
Sample: ID Course Status
1 23 4
2 23 5
3 23 2
4 23 1
Assuming that was the database table the Course is [#CourseNumber] is 23 . We would want to get the last row of the Course number which is [4] and we would want the status which is [1] since the status is not 2 for that last row we would just return a 0 and we would only return a 1 if the last row of the course number has a status of 1 . This is a sql function inside of a view . I try to leave out some of things that might not be necessary since it is a large View and function .
You want to self join to the table to determine the maximum ID value to find the record you want. You say add 1 to #MyValue and later you say to set #MyValue = 1. This will add 1 to #MyValue. If you want #MyValue = 1, change the case statement removing the #MyValue parameter.
DECLARE #MyValue INT = 0
, #CourseNumber INT = 23
SELECT #MyValue = CASE WHEN t0.Status = 2 THEN #MyValue + 1 ELSE #MyValue END
FROM MyTasks t0
JOIN
(
SELECT Couse
, MAX(ID) MAXID
FROM MyTasks
WHERE Course = #CourseNumber
) t1 ON t0.Course = t1.Course AND t0.ID = t1.MAXID
I currently have data like so:
Product_ID IND 1_Revenue 2_Revenue Revenue_Code Channel
1 S $50. $75. 1 E
1. S $50. $75. 2 SE
2. P $100. $0. 1 E
3. S $400. $60. 1 SE
3. S $400. $60. 2 S
I am trying to pick when IND=S, give me the row with the highest revenue if the channel= SE. the revenue code refers to the fields 1_Revenue and 2_Revenue.
So in this case I’d expect the output to have 2nd row and the 4th row.
I’ve tried multiple things and nothing has worked. What is the best solution?
As per our understanding a simple where clause is sufficient to get your result like:
select Product_ID, IND, 1_Revenue, 2_Revenue, Revenue_Code, Channel
from yourtable
where IND = 'S' and Channel = 'SE'
If anything else is required then kindly mention it.
I don't quite understand what is meant by the highest revenue. Based on your description, if you just apply a filter to pick rows where IND = S and channel = SE then won't you get rows 2 and 4 out? (as follows)
data want;
set have;
if IND = 'S' and channel = 'SE';
run;
or if you want to use SQL
PROC SQL;
create table want as
select * from have where IND = 'S' and channel = 'SE';
quit;
I'm attempting to create a stored procedure that implements an exclusive or to return a specific record from a table (BasisValues).
Here's the table:
Basis Value
1 4.25
2 1.25
3 99.00
0 0.00
I've designed a stored procedure to return a BasisValue as follows:
CREATE PROCEDURE GetBasisValue
#Basis VARCHAR(2)
AS
BEGIN
SELECT Value FROM BasisValues
WHERE ( BASIS = #Basis) OR
((BASIS <> #Basis) AND (Basis = '0'))
END
As you can see above, I'm attempting to SELECT the Value field from the BasisValues table according to the following logic:
1. If the Basis is equal to the #Basis passed into the stored procedure, return that value.
2. If the Basis is not equal to the #Basis passed into the stored procedure, then return the value of Basis = 0.
However, given a 'Basis' that exists in the table, I end up getting two records. For example, if I called GET_BASIS_VALUES with a #Basis of 2 I get:
Basis Value
2 1.25
0 0.00
In other words, I always return the '0' record no matter what. What am I missing in terms of my logic?
You want to use order by to select the prefered value:
SELECT TOP 1 Value
FROM BasisValues
WHERE ( BASIS = #Basis) OR
((BASIS <> #Basis) AND (Basis = '0'))
ORDER BY (case when BASIS = #BASIS then 1 else 0 end) DESC;
This query (without the top) will return one or two values, based on what you describe in the question. It will then order the results, so the the non-default is first. By choosing the first row after the order by, you get a match first, and then the default if there is no match.
CREATE PROCEDURE GetBasisValue
#Basis VARCHAR(2)
AS
BEGIN
SELECT ISNULL((SELECT Value FROM BasisValues
WHERE (BASIS = #Basis)), '0.00')
END
An answer does not have to depend on the value of your default basis or how it compares to other available choices, you can get this with the following
SELECT Value FROM BasisValues
WHERE BASIS = CASE WHEN EXISTS (SELECT * FROM BasisValues WHERE #Basis = BASIS ) THEN #Basis ELSE 0 END
What this says is, if the basis you asked for exists, return the value for that record, if it doesn't, return the value for record 0
In my table I have a "name" column and a "status" column.
the status is either true or false.
And another table contains a number which is a total amount.
The result that I want to get is a table with two columns:
name | status
and and example of a data:
a | available
a | available
a | not available
a | not available
a | available
when "a" is in the name column and the availability is the status column.
The total amount from the second table indicates the total number of "a" rows that i need to have, and the status depends on the true/false from the status column in the original table.
If the status is "true" I need to write "available" and when "false" then "not available".
If the total amount value is bigger than the data I have in the first table, I need to add rows according to the total amount with the status "available".
For example, If I have 3 records of "a", when one has the status "true" and the other two have the status "false", and the total amount is 4, In the result I need to get 4 rows with the name "a", 2 of them "available" and 2 "not available" (the given 3 rows, plus one row to make it 4).
My question is, how can I change the value according to the data in the table? (Write available/ not available)
And how can I add a certain amount of rows with preset values (same name as before, and "available" status)?
"...how can I change the value according to the data in the table?"
You can use CASE() to test for the value of the column.
SELECT name,
CASE WHEN status = 'true'
THEN 'available'
ELSE 'not available'
END status
FROM tableName
For future answer-seekers:
To get the 'fake' rows, one way is to use are recursive CTE:
WITH Expanded_Data as (SELECT Counted_Data.name,
CAST('true' as VARCHAR(5)) as status,
Counted_Data.count + 1 as count,
Total.count as limit
FROM (SELECT name, COUNT(*) as count
FROM Data
GROUP BY name) Counted_Data
JOIN Total
ON Counted_Data.name = Total.name
AND Counted_Data.count < Total.count
UNION ALL
SELECT name, status, count + 1, limit
FROM Expanded_Data
WHERE count < limit)
SELECT name, CASE WHEN status = 'true'
THEN 'available'
ELSE 'not available' END
FROM (SELECT name, status
FROM Data
UNION ALL
SELECT name, status
FROM Expanded_Data) d;
(have a working SQL Fiddle example.)
I'm a little worried about the initial duplication in the source data though; I can only hope there is more 'unique' information as well.
I have three tables similar to the following:
tblInvoices: Number | Date | Customer
tblInvDetails: Invoice | Quantity | Rate | Description
tblPayments: Invoice | Date | Amount
I have created a query called exInvDetails that adds an Amount column to tblInvDetails:
SELECT tblInvDetails.*, [tblInvDetails.Quantity]*[tblInvDetails.Rate]* AS Amount
FROM tblInvDetails;
I then created a query exInvoices to add Total and Balance columns to tblInvoices:
SELECT tblInvoices.*,
(SELECT Sum(exInvDetails.Amount) FROM exInvDetails WHERE exInvDetails.Invoice = tblInvoices.Number) AS Total,
(SELECT Sum(tblPayments.Amount) FROM tblPayments WHERE tblPayments.Invoice = tblInvoices.Number) AS Payments,
(Total-Payments) AS Balance
FROM tblInvoices;
If there are no corresponding payments in tblPayments, the fields are null instead of 0. Is there a way to force the resulting query to put a 0 in this column?
Use the nz() function, as in nz(colName, 0). This will return colName, unless it is null, in which case it will return the 2nd paramter (in this case, 0).
I think that Default Value likes Field location. For example, My Field is ID that is in tblEmpPCs table, I used a criteria below.
IIf([Forms]![frmSearchByEmp]![cboTurul]="ID",[forms]![frmSearchByEmp]![Ezemshigch],[tblEmpPCs].[ID])
There are False value is Default value of Criteria.
I uploaded a picture of my query criteria. It may help you.