BAQ to evaluate consecutive rows in table

Occasionally receiving errors happen, causing unexpected changes in part costs. I am trying to write a BAQ to compare the highest-numbered PartTran.TranNum with the next highest one, flagging entries with unit cost changes greater than a threshold.

To this end, I’ve created a BAQ with a main subquery to pull in recent PartTran rows for a part in question. And I’ve created a subquery (joined to PartTran with matches on Company & PartNum). What I want from the subquery’s result set is not “All” but “Top 2” (and I’ll filter out the 1st row in the join), ordering the results on TranNum.

I’m doing something wrong and can’t make it work. Ideas welcome.

If I apply a Top option and a row number quantity (even a very large number), the query results set is nothing. If I leave “All” in place, naturally I get too many rows.

Thanks in advance!

You want top 2 from each of the part numbers? Or just 2 rows period?

Top 2 rows for each part number, enabling cost comparisons between them.
(In building the query and with an attempt to keep the results set manageable, I’m currently filtering on one specific part. But once I’m able to compare the cost associated with the newest row and the second row, then I’ll remove the part number filter and see several thousand results–one for each part in my inventory.

Here’s one way to do it. Make 2 subqueries, on returning top 2 rows, the other returning only the top row. Then join those to on company and part number, and where trannum <> to trannum. This will get your 2 rows into one, and you can attach your parttran cost information to come along for the ride.

This will only work though if you are filtering by the part number all of the way through. If you want to be able to make this work with more than one part number at a time, I think you will need to use windowing.

select 
	[TwoRows].[PartTran_Company] as [PartTran_Company],
	[TwoRows].[PartTran_PartNum] as [PartTran_PartNum],
	[TwoRows].[PartTran_TranNum] as [PartTran_TranNum],
	[OneRow].[PartTran1_Company] as [PartTran1_Company],
	[OneRow].[PartTran1_PartNum] as [PartTran1_PartNum],
	[OneRow].[PartTran1_TranNum] as [PartTran1_TranNum]
from  (select top (2)  
	[PartTran].[Company] as [PartTran_Company],
	[PartTran].[PartNum] as [PartTran_PartNum],
	[PartTran].[TranNum] as [PartTran_TranNum]
from Erp.PartTran as PartTran
where (PartTran.PartNum = 'WN06C')
order by PartTran.TranNum Desc)  as TwoRows
inner join  (select top (1)  
	[PartTran1].[Company] as [PartTran1_Company],
	[PartTran1].[PartNum] as [PartTran1_PartNum],
	[PartTran1].[TranNum] as [PartTran1_TranNum]
from Erp.PartTran as PartTran1
where (PartTran1.PartNum = 'WN06C')
order by PartTran1.TranNum Desc)  as OneRow on 
	TwoRows.PartTran_Company = OneRow.PartTran1_Company
	and TwoRows.PartTran_PartNum = OneRow.PartTran1_PartNum
	and  not TwoRows.PartTran_TranNum = OneRow.PartTran1_TranNum

So if you make a calculated field like this to get your row numbers, this will number your rows 1-x with the max being first. Then you can filter by row number 1 and 2. I would still probably do 2 nearly identical subqueries with this field in them, then filter one by RowNumber 1 and the second by RowNumber 2 and then you can do the math that you need on one row.

Row_Number() over(partition by PartTran.PartNum order by PartTran.TranNum desc)

Here’s the full query. From here, you should be able to tack on your cost information from part tran

select 
	[TwoRows].[PartTran_Company] as [PartTran_Company],
	[TwoRows].[PartTran_PartNum] as [PartTran_PartNum],
	[TwoRows].[PartTran_TranNum] as [PartTran_TranNum],
	[OneRow].[PartTran1_Company] as [PartTran1_Company],
	[OneRow].[PartTran1_PartNum] as [PartTran1_PartNum],
	[OneRow].[PartTran1_TranNum] as [PartTran1_TranNum],
	[TwoRows].[Calculated_RowNumbers] as [Calculated_RowNumbers],
	[OneRow].[Calculated_RowNumbers] as [Calculated_RowNumbers01]
from  (select 
	[PartTran].[Company] as [PartTran_Company],
	[PartTran].[PartNum] as [PartTran_PartNum],
	[PartTran].[TranNum] as [PartTran_TranNum],
	(Row_Number() over(partition by PartTran.PartNum order by PartTran.TranNum desc)) as [Calculated_RowNumbers]
from Erp.PartTran as PartTran)  as TwoRows
inner join  (select 
	[PartTran1].[Company] as [PartTran1_Company],
	[PartTran1].[PartNum] as [PartTran1_PartNum],
	[PartTran1].[TranNum] as [PartTran1_TranNum],
	(Row_Number() over(partition by PartTran1.PartNum order by PartTran1.TranNum desc)) as [Calculated_RowNumbers]
from Erp.PartTran as PartTran1)  as OneRow on 
	TwoRows.PartTran_Company = OneRow.PartTran1_Company
	and TwoRows.PartTran_PartNum = OneRow.PartTran1_PartNum
	and ( OneRow.Calculated_RowNumbers = 1  )

where (TwoRows.Calculated_RowNumbers = 2)

Hi @Banderson,
If I had a local instance of Epicor E10 and could point Microsoft SSMS at it, I believe your SQL would work perfectly or get me 98% of the way home. I’m a cloud customer and thus must work through the BAQ tool. The General tab in BAQ shows me the Query Phrase and I so much wish I could edit in there, but I can’t.

Either I must filter by part in the sub-queries (like your first example) or learn a few more BAQ tricks. Specifically, I’m hung up on this clause:

Can you provide some guidance?

Many thanks!

You can type that into a calculated field. I didn’t write the SQL from scratch (well I typed the calculated field), I did all of it using the BAQ tool. No external references is what I mean. Remember you can use most any SQL functions in the BAQ. The menu on the side only has the basic stuff, but you can use much more.

BAQ sample.baq (54.0 KB)

Hi @Banderson,

I apologize but am still stuck, this time on the nested subquery,

I simply don’t know how to generate this syntax within a BAQ. I’m already in your debt, and am hoping you’ll be willing to enlighten me a bit further.

Many thanks.

Did you try to import the BAQ that I uploaded?

Here’s the query in screen shots.

First bring in the partran table.

On the display fields select Company, PartNum, TranNum.

Then click on calculated field.

Create a new calculated field, call it whatever you want, type integer.

Then type what’s shown below. “Row_Number()”, “Over” and “Partition by” do no show up the menus, but they are allowed to be used. So have to type that part. You can double click on the fields that you want to use (in fact I recommend doing that).

At this point, go ahead and test the query. You will see that if you sort by part number, you will see row numbers that start over at each part number.

image

(continuing in the next post)

Hi @Banderson,
I failed to notice that you had uploaded the code; mea culpa.

You’d make a good teacher with all your patience.

Your profile seems to show that you’re located a few hours west of me; I rarely get to your section of the state. But if you’re ever in the Twin Cities, I’d be happy to treat you to lunch.

Many thanks!

Paul

The next thing you want to do is make this a subquery instead of a top level. We do that in the subquery options section.
image

Now in the same screen we can make the next subquery, which is going to be basically the same thing.

image

Now we go to the phrase build tab, grab the PartTran Table again. Since we already have one, a popup will appear, saying “you have to call it something different, dude” (My words not epicors) You can accept and it will call PartTran1

image

Grab the same fields as we did the first time.

And you now have 2 subqueries that are bringing you back the same thing.

(continuing in the next post)

Yeah, I’ve met you before at the EUG meetings. Happy to help.

Now we go back to the subquery options and we are going to create our top level.

image

We go back to the Phrase build. To see the subqueries, you need to toggle this button. Both of them should be there, bring them both onto the canvas.

Join them by Company and Partnumber.

Set the criteria for one of them to RowNumber = 2, and the other RowNumber = 1


image

Go to your display fields, grab all of the fields

Now go test, and you will see row number one info next to row number 2 info.

Now that you are returning the rows you need, you can go back and get the information that you need to come along for the ride (like unit cost) and do a calculated field on the top level to see your difference.

Does that make sense?