1

I am trying to replicate the following type of SQL query that you can perform in SQL Server...the important part here is the WHERE clause: Select InventoryCD from InventoryItem WHERE InventoryCD IN ('123123', '154677', '445899', '998766')

It works perfectly using the IN3<> operator and a series of string constants: i.e. And<InventoryItem.inventoryCD, In3<constantA,constantB,constantC>,

However, I need to be able to do this with an arbitrarily long list of values in an array, and I need to be able to set the values dynamically at runtime.

I'm not sure what type I need to pass in to the IN<> statement in my PXProjection query. I have been playing around with the following approach, but this throws a compiler error.

public class SOSiteStatusFilterExt : PXCacheExtension<SOSiteStatusFilter>
{
    public static bool IsActive()
    {
        return true;
    }
    public abstract class searchitemsarray : PX.Data.IBqlField
    { 
    }
    [PXUnboundDefault()]
    public virtual string[] Searchitemsarray { get; set; }

}

I think maybe I need an array of PXString objects? I'm really not sure, and there isn't any documentation that is helpful. Can anyone help?

This shows how to do it with a regular PXSelect: https://asiablog.acumatica.com/2017/11/sql-in-operator-in-bql.html

But I need to be able to pass in the correct type using Select2...

  • You can use any PXSelect methods, the example you link to explains how to do it with 'Required' BQL operator. So you need to do In3> and pass the parameter value in Select method parameters. – Hugues Beauséjour Nov 03 '21 at 22:29
  • Try passing an array of Object like in the example as parameter. Or format your array to a string array type if it doesn't work. – Hugues Beauséjour Nov 03 '21 at 22:30
  • The DAC type is not good though. String array is not for DAC. You need to change that and convert the data types. – Hugues Beauséjour Nov 03 '21 at 22:34
  • In Select2 statement.... I am using this in Where statement: And> What DAC field type do I use for searchitemsarray? BQL requires some kind of DAC field type...but I don't know what to convert my string array to for DAC presentation. Do you think you could provide a quick example using Select2, and any array of strings passed into IN<> at runtime? – user1576945 Nov 03 '21 at 22:40
  • Use example from the link: https://asiablog.acumatica.com/2017/11/sql-in-operator-in-bql.html – Hugues Beauséjour Nov 04 '21 at 04:15
  • Example isn't suited for Select2. Required must be used and Current is not a substitute. This is for PXSelect methods not Select2. Select2 will resolve to null at best in what you do. No equivalent here, arrays are not valid DAC field types. – Hugues Beauséjour Nov 04 '21 at 04:27
  • So is the answer here that the IN operator does not work with Select2 queries and therefore doesn't work with PXProjection? Since PXProjection is used to do "advanced" SQL queries in Acumatica, doesn't this kind of tie one hand behind our back in certain situations (it is certainly making something that would take me 5 seconds in native SQL take 5 days instead). Alternatives seem to be to use a bunch of OR conditions with nullable fields, or to create some kind of virtual table and join it to my actual table to simulate an IN type function. – user1576945 Nov 04 '21 at 04:45
  • Required parameter resolves to a list of constant so it is valid. You are not attempting to use the BQL operator as designed. Projections don't matter here. It sounds like you are using the operator in a DAC extension and expecting the operator to generate sub-queries. IN3 appends list of constants, you are not using it like that. – Hugues Beauséjour Nov 04 '21 at 15:17
  • Select2 doesn't support Required parameter, it has no Select method to pass parameter. Current parameter is not Required parameter. You need to use IN3 operator like it was designed to with up to 10 static constants in DAC extension or in graph with PXSelect methods and Required operator/parameter. – Hugues Beauséjour Nov 04 '21 at 15:22
  • And DAC fields of array types are not valid. The solution you are describing needs to be changed to the one in the link example if you want to use IN3 operator. – Hugues Beauséjour Nov 04 '21 at 15:30
  • I feel like we are talking in circles here. I acknowledged in my original post that In3 is a solution if you want to set the values before runtime. There is no way to set the In3<> values at runtime though, and In3 has a maximum number of values (I think it's 3 values total... this is undocumented). I am fine if there is some equivalent we can use... in native SQL, you could do select * from mytable where id IN (select '1', '2', '3', '4'). But to have no ability to subselect based on IN at runtime is super annoying and there doesn't seem to be any programmatic reason for it. – user1576945 Nov 04 '21 at 16:11
  • I added this to Acumatica Forums (I don't currently have access to Product Ideas): https://community.acumatica.com/integrations-and-isv-solutions-116/please-uncripple-the-acumatica-bql-in-operator-so-it-can-be-used-with-select2-type-queries-7198 It gives some further explanation. I profiled this query with SQL Profiler and it does compile and is parameterized when using Select2, there just is no way to get the values into the variable. So this is just broken. If it can't work with Select2, then it should throw a compiler error when you try to use it in that context. – user1576945 Nov 04 '21 at 16:56
  • With regards to discussion of constants vs not, constants are set before runtime. That is what defines a constant. The use case in the Acumatica blog post I referenced is to set the values during runtime using a String[] array, so that is setting IN<> operator variables at runtime. Select2 just has no mechanism to do the same thing that you can do with PXSelect. There is clearly no programmatic reason for this, it is just something the programmers decided not to implement for whatever reason. The fact that it compiles with Select2 makes it more bug than "feature". – user1576945 Nov 04 '21 at 17:24
  • I'm going by the constructor you are using for IN3 in DAC extension. Documentation specifies you need to use constant for that constructor: https://help-2021r2.acumatica.com/(W(2))/Help?ScreenId=ShowWiki&pageid=b20a5bf2-a586-3765-1ce3-7e46453e23e9 – Hugues Beauséjour Nov 04 '21 at 19:04
  • The other option is to pass Required parameter. This one specifies it must use Select() method, not the Select2 operator: https://help-2021r2.acumatica.com/(W(2))/Help?ScreenId=ShowWiki&pageid=ea358533-f102-21d0-d4b5-cc4795a3cbc0 – Hugues Beauséjour Nov 04 '21 at 19:05
  • The only features of these classes are the documented one, it does not do anything beyond. – Hugues Beauséjour Nov 04 '21 at 19:06
  • Nope, you're still missing it. What you referenced says you have to use PXSelect. If it starts with PX...then it is attached to a single table by definition. Select queries (no PX at the start) are used for PXProjection, because they can use full range of sql type operators to create a more complex query. So the problem is that the IN operator is broken when using queries that start with Select (Select, Select2, Select3, Select4...take your pick). They will work with anything that starts with PXSelect. The problem here is that this is barely documented, per the back and forth. – user1576945 Nov 04 '21 at 19:18
  • The bottom line here is that one of the most basic SQL Operators is broken for your "advanced" query functionality. The only way to use that functionality is to set the values as constants before runtime. It's your platform, you can decide to do or not do whatever you want with the query language. But my point is that this is something that would be dead simple to implement in a Stored Procedure with parameterized values, so why can't it work in your advanced query functionality? – user1576945 Nov 04 '21 at 19:44
  • https://stackoverflow.com/questions/337704/parameterize-an-sql-in-clause Here is a code example for parameterizing a SQL In Clause. There is even a secondary answer courtesy of Joel Spolsky (StackOverflow founder)! – user1576945 Nov 04 '21 at 19:56

2 Answers2

2

For reference I will post here the example mentioned by Hugues in the comments.

If you need to generate a query with an arbitrary list of values generated at runtime like this:

Select * from InventoryItem InventoryItem
Where InventoryItem.InventoryCD IN ('123123', '154677', '445899', '998766')
Order by InventoryItem.InventoryCD

You would write something like this:

Object[] values = new String[] { "123123", "154677", "445899", "998766" };

InventoryItem item = PXSelect<InventoryItem,
      Where<InventoryItem.inventoryCD,
      In<Required<InventoryItem.inventoryCD>>>>.Select(Base, values);

Please note that In<> operator is available only with Required<> parameter and you need to pass array of possible values manually to Select(…) method parameters. So you need to fill this array with your list before calling the Select method.

Also, the Required<> parameter should be used only in the BQL statements that are directly executed in the application code. The data views that are queried from the UI will not work if they contain Required<> parameters.

markoan
  • 498
  • 5
  • 14
  • I referenced the same link you reference in my original question, which has the same answer you posted. The problem is that it isn't an answer to the question. My question asks whether we can use the IN Operator with a PXProjection, which is a SQL view bound to many database tables, not just one. The PXProjection uses the Select,Select2, Select3, Select4, Select5 or Select6 line of commands (in traditional BQL). In order to answer the question, we need to make the IN Operator work with a PXProjection/Select style query. And so far there seems to be no way to do that. – user1576945 Nov 06 '21 at 22:42
  • 1
    You are right, this won't work in the PXProjection attribute directly, as the Required<> class needs to be executed in code, but I see no reason why it wouldn't work with any of the SelectX classes. It would help if you could add a complete example of your Projection and BQL in your question. It's possible it can be handled by overriding the enumeration method of your projection. – markoan Nov 08 '21 at 15:12
  • Can be used with a PXProjection DAC but not inside the DAC. – Hugues Beauséjour Nov 09 '21 at 17:37
  • https://gist.github.com/alaskancode/1a45045bd6351c6e8f727f7a92b6ce96 This code, when put into an Extension Library, will override the SQL view in the "Add Items" product search screen inside the Sales Order entry screen. I want to use the IN<> operator with this Select2 query, and then pass in values, the same way that is currently being done with Or, Equal, (as an example) You can use the IN<> operator with Select2, but there is no way to pass in the value, because it only works with Required, and Required needs PXSelect. – user1576945 Nov 09 '21 at 21:51
0

I ended up creating 100 variables and using the BQL OR operator, i.e.

  And2<Where<InventoryItem.inventoryCD, Equal<CurrentValue<SOSiteStatusFilterExt.Pagefilter1>>,
        Or<InventoryItem.inventoryCD, Equal<CurrentValue<SOSiteStatusFilterExt.Pagefilter2>>,
        Or<InventoryItem.inventoryCD, Equal<CurrentValue<SOSiteStatusFilterExt.Pagefilter3>>,
        Or<InventoryItem.inventoryCD, Equal<CurrentValue<SOSiteStatusFilterExt.Pagefilter4>>,

 etc...etc...

You can then set the value of Pagefilter1, 2, etc inside of the FieldSelecting event for SOSiteStatusFilter.inventory, as an example. The key insight here, which isn't that obvious to the uninitiated in Acumatica, is that all variables parameterized in SQL Server via BQL are nullable. If the variable is null when the query is run, SQL Server automatically disables that variable using a "bit flipping" approach to disable that variable in the SQL procedure call. In normal T-SQL, this would throw an error. But Acumatica's framework handles the case of a NULL field equality by disabling that variable inside the SQL procedure before the equality is evaluated.

Performance with this approach was very good, especially because we are querying on an indexed key field (InventoryCD isn't technically the primary key but it is basically a close second).

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459