Problems with the BQL "IN<>" statement - acumatica

The requirement I have is to get a list of all discount codes defined in an instance and which ones a particular customer is currently assigned to, in the case given CustomerID=28. I further have to include only discount codes that naturally will be applicable to customers. There are only 3 of these; "Customer", "Customer and Item", "Customer and Item price Class". These are ARDiscount.ApplicableTo containing "CU", "CP","CI"
Select a.CompanyID, a.DiscountID, a.DiscountSequenceID, b.ApplicableTo, c.CustomerID
From DiscountSequence a
Join ARDiscount b On a.CompanyID = b.CompanyID and a.DiscountID = b.DiscountID
Left Outer Join DiscountCustomer c On a.CompanyID = c.CompanyID
And a.DiscountID = c.DiscountID
And a.DiscountSequenceID = c.DiscountSequenceID
And (IsNull(c.CustomerID,0) = 0 OR c.CustomerID = 72)
Where a.CompanyID = 2
And b.ApplicableTo In ('CU','CP','CI')
Order By a.DiscountID, a.DiscountSequenceID
I created data view delegate to return the 4 columns I need to display and in the view I created
to read the data like the SQL query above I used the BQL "IN<>" statement like this. The method was taken directlty from a blog post found here :
https://asiablog.acumatica.com/2017/11/sql-in-operator-in-bql.html
Object[] applicableTovalues = new String[] { "CP","CI","CU" }; // Customer and Price Class // Customer and Item// Customer
var Results = PXSelectJoin<DiscountSequence
, InnerJoin<ARDiscount, On<DiscountSequence.discountID, Equal<ARDiscount.discountID>>
, LeftJoin<DiscountCustomer, On<DiscountSequence.discountID, Equal<DiscountCustomer.discountID>,
And<DiscountSequence.discountSequenceID, Equal<DiscountCustomer.discountSequenceID>,
And<Where<DiscountCustomer.customerID, Equal<Current<Customer.bAccountID>>,
Or<DiscountCustomer.customerID, IsNull>>>>>>>
, Where<DiscountSequence.discountID, IsNotNull
, And<ARDiscount.applicableTo, In<Required<ARDiscount.applicableTo>>>>
, OrderBy<Asc<DiscountSequence.discountID, Asc<DiscountSequence.discountSequenceID>>>
>.Select(Base, applicableTovalues);
The problem is that the resulting SQL server select statement caught with TRACE only includes the first of the three IN values (''CU'') leaving (CI and CU) out.
I was expecting all three values in the IN statement like this : AND [ARDiscount].[ApplicableTo] IN ( ''CP'', ''CI'', ''CU'')
exec sp_executesql N'SELECT [DiscountSequence].[DiscountID], [DiscountSequence].[DiscountSequenceID], [DiscountSequence].[LineCntr],
<snip>
[DiscountCustomer].[CreatedDateTime], [DiscountCustomer].[LastModifiedByID], [DiscountCustomer].[LastModifiedByScreenID], [DiscountCustomer].[LastModifiedDateTime]
FROM [DiscountSequence] [DiscountSequence] INNER JOIN [ARDiscount] [ARDiscount] ON ( [ARDiscount].[CompanyID] = 2) AND [DiscountSequence].[DiscountID] = [ARDiscount].[DiscountID]
LEFT JOIN [DiscountCustomer] [DiscountCustomer] ON ( [DiscountCustomer].[CompanyID] = 2) AND [DiscountSequence].[DiscountID] = [DiscountCustomer].[DiscountID]
AND [DiscountSequence].[DiscountSequenceID] = [DiscountCustomer].[DiscountSequenceID] AND ( [DiscountCustomer].[CustomerID] = #P0 OR [DiscountCustomer].[CustomerID] IS NULL )
WHERE ( [DiscountSequence].[CompanyID] = 2)
AND ( [DiscountSequence].[DiscountID] IS NOT NULL
AND [ARDiscount].[ApplicableTo] IN ( ''CU''))
ORDER BY [DiscountSequence].[DiscountID], [DiscountSequence].[DiscountSequenceID]
OPTION(OPTIMIZE FOR UNKNOWN) /* AR.30.30.00 */',N'#P0 int',#P0=39

The issue is passing the array into the 'params' parameter. It thinks that you are passing a list of parameters into the bql query instead of a single array as a parameter.
If you cast it as follows it should work:
.Select(Base, (object)applicableTovalues);

Related

Get the names of a dynamic product group with a subscriber in Shopware 6.1

how can I get the names of the dynamic product groups of a product?
Especially the names of that groups. The names are in the table 'product_stream_translation'.
I have a subscriber and have added this Criteria. When I am doing it like this:
$criteria->addFilter(new EqualsFilter('id', $id));
$criteria->addAssociation('streams');
$dynamicProductGroups = $this->productRepository->search($criteria, $context)->getEntities();
I just got back an empty streamEntity.
#streams: Shopware\Core\Content\ProductStream\ProductStreamCollection {#11805 ▼
#elements: []
#extensions: []
}
When I am doing it like this:
$criteria->addFilter(new EqualsFilter('productId', $id));
$dynamicProductGroups = $this->productStreamMappingRepository->searchIds($criteria, $context);
I just got back the Id I put in:
product_stream_mapping
I wonder how I will get the name of the dynamic product group.
With a query I can get all the assign 'product_stream_id's from the table 'product_stream_mapping' like this:
SELECT * FROM product_stream_mapping WHERE product_id =0x000000000000000000123b313030524b
And then get the associated name of the dynamic product group. With is like this:
SELECT psm.product_id, psm.product_stream_id, p.product_number, pst.name
FROM product_stream_mapping psm
JOIN product_stream_translation pst ON pst.product_stream_id = psm.product_stream_id
JOIN product p ON p.id = psm.product_id
WHERE psm.product_id = 0x000000000000000000123b313030524b
How can I get it in the Subscriber?
Do I have to use criteria or do I have to user a repository?
This line is wrong:
$dynamicProductGroups = $this->productRepository->search($criteria, $context)->getEntities();
It won't return dynamic product groups but a product collection. To get a product stream collection (dynamic product groups) replace it with:
/** #var ProductEntity $product */
$product = $this->productRepository->search($criteria, $context)->first();
$dynamicProductGroups = $product->getStreams();
Then you can read the names of the streams with:
$names = array_values(
$dynamicProductGroups->map(fn (ProductStreamEntity $productStream) => $productStream->getName())
);

Use coalesce in a subquery while creating a view

I am currently trying to make a view in order to show the name of the clients that have purchased a car (new or used) and need to show "NA" when the client name is NULL.
IF OBJECT_ID ('V_PrixSuperieurMoyenne','V') IS NOT NULL
DROP VIEW V_PrixSuperieurMoyenne;
GO
CREATE VIEW V_PrixSuperieurMoyenne
AS
(
SELECT
CS.[State],
CI.ListPrice,
CI.CarInventoryID,
COALESCE(CONCAT(C.Name, ' ', C.FirstName), 'NA') AS 'Nom du Client',
(
SELECT AVG(CI.ListPrice)
FROM CarInventory CI
INNER JOIN CarState CS ON CS.CarStateID = CI.CarStateID
WHERE CS.State = 'New'
) AS 'Prix Moyen Neuf'
FROM
CarInventory CI
INNER JOIN CarState CS ON CS.CarStateID = CI.CarStateID
LEFT JOIN Invoice I ON I.CarInventoryID = CI.CarInventoryID
LEFT JOIN Client C ON C.ClientID = I.ClientID
GROUP BY
CS.State,
CI.ListPrice,
CI.CarInventoryID,
CONCAT(C.Name , ' ', C.FirstName)
)
GO
SELECT *
FROM V_PrixSuperieurMoyenne
ORDER BY 1,2
GO
Here is my code, however it seems like the COALESCE doesn't work as when I try to run the code, when the names are NULL, it only shows an empty string. I need it to show 'NA'.
The CONCAT function will return ' ' when C.Name and C.FirstName are NULL. However, if you replace CONCAT with string additions, you will get NULL as you expect, because a single NULL value in a line of string additions nulls out the entire string.
-- Returns 'NA' when C.Name or C.FirstName are NULL.
COALESCE(C.Name + ' ' + C.FirstName,'NA')
You could also replace COALESCE with ISNULL since you're only checking nullity of a single item:
ISNULL(C.Name +' ' + C.FirstName,'NA')
Be sure to make this change in your GROUP BY, too.
COALESCE is typically used when you want to check whether multiple items are NULL.

Insert data based on Where condition in Neo4j

I want to insert data based on other lebel/collection. I have 2 lebel/collection ( unit, user ) and they have 1 relation (Business) between them, And I want to insert data into unit based on their relationship. My cypher query is given below:
MATCH (u:Units)<-[:Business]-(s:Users)
WHERE s.id = 'some-user-id'
WITH count(u) as numOfUnit
// return number of units connected with user
// if numOfUnit is smaller then 2
// insert/merge new data into Units lebel/collection
// with relation between them
MERGE ( bu:Units {name:'some-name-01', info:'some-info-01' })
WHERE numOfUnit < 2
ON CREATE SET
bu.id = '${uuid()}',
bu.created = '${moment().toISOString()}'
ON MATCH SET
bu.updated = '${moment().toISOString()}'
WITH bu as bu
MATCH ( bs:Users {id: 'some-user-id' })
MERGE (bs)-[r:Business]-(bu)
RETURN properties(bu)
After running above query, It shown following error:
{ Neo4jError: Invalid input 'H': expected 'i/I' (line 10, column 18
(offset: 377))
" ON CREATE SET"
^
at Neo4jError.Error (native)
at new Neo4jError (../../../../node_modules/neo4j-driver/lib/v1/error.js:76:132)
at newError (../../../../node_modules/neo4j-driver/lib/v1/error.js:66:10)
at Connection._handleMessage (../../../../node_modules/neo4j-driver/lib/v1/internal/connector.js:355:56)
at Dechunker.Connection._dechunker.onmessage (../../../../node_modules/neo4j-driver/lib/v1/internal/connector.js:286:12)
at Dechunker._onHeader (../../../../node_modules/neo4j-driver/lib/v1/internal/chunking.js:246:14)
at Dechunker.AWAITING_CHUNK (../../../../node_modules/neo4j-driver/lib/v1/internal/chunking.js:199:21)
at Dechunker.write (../../../../node_modules/neo4j-driver/lib/v1/internal/chunking.js:257:28)
at NodeChannel.self._ch.onmessage (../../../../node_modules/neo4j-driver/lib/v1/internal/connector.js:259:27)
at TLSSocket.<anonymous> (../../../../node_modules/neo4j-driver/lib/v1/internal/ch-node.js:308:16)
code: 'Neo.ClientError.Statement.SyntaxError',
name: 'Neo4jError' }
The docs about WHERE clause says:
WHERE adds constraints to the patterns in a MATCH or OPTIONAL MATCH
clause or filters the results of a WITH clause.
That is: WHERE cannot be used in conjunction with MERGE.
As stated in comments, your query should work putting the WHERE condition after the WITH clause, because you can use WHERE to filter results of a WITH.
MATCH (u:Units)<-[:Business]-(s:Users)
WHERE s.id = 'some-user-id'
WITH count(u) as numOfUnit
WHERE numOfUnit < 2
MERGE ( bu:Units {name:'some-name-01', info:'some-info-01' })
ON CREATE SET
bu.id = '${uuid()}',
bu.created = '${moment().toISOString()}'
ON MATCH SET
bu.updated = '${moment().toISOString()}'
WITH bu as bu
MATCH ( bs:Users {id: 'some-user-id' })
MERGE (bs)-[r:Business]-(bu)
RETURN properties(bu)

Grouping a dictionary NSFetchRequest by object ID

I need to return a list of objects along with a count of its related objects. It doesn't seem to be possible to do this in a single dictionary fetch request as I am unable to group the fetch results by objectID.
let objectIDExpression = NSExpressionDescription()
objectIDExpression.name = "objectID"
objectIDExpression.expression = NSExpression.expressionForEvaluatedObject()
objectIDExpression.expressionResultType = NSAttributeType.ObjectIDAttributeType
let countExpression = NSExpressionDescription()
countExpression.name = "count"
countExpression.expression = NSExpression(forFunction: "count:", arguments: [NSExpression(forKeyPath: "entries")])
countExpression.expressionResultType = .Integer32AttributeType
let fetchRequest = NSFetchRequest(entityName: "Tag")
fetchRequest.resultType = .DictionaryResultType
fetchRequest.propertiesToFetch = [objectIDExpression, countExpression]
fetchRequest.propertiesToGroupBy = [objectIDExpression]
var error: NSError?
if let results = self.context.executeFetchRequest(fetchRequest, error: &error) {
println(results)
}
When this request executes it errors with:
'Invalid keypath expression ((<NSExpressionDescription: 0x7f843bf2d470>), name objectID, isOptional 1, isTransient 0, entity (null), renamingIdentifier objectID, validation predicates (
), warnings (
), versionHashModifier (null)
userInfo {
}) passed to setPropertiesToFetch:'
I also tested just passing the "objectID" expression name, but that also fails.
Is there therefore no way to group by object ID?
You can get the required count without using propertiesToGroupBy. CoreData seems to infer the correct scope for the count and uses a sub-SELECT instead (strangely, only if the fetch includes an attribute as well as the objectID and count, see below). For example, I have Tag many-many with Items:
First attempt
I can fetch tagName and the count of items as follows:
NSFetchRequest *fetch = [NSFetchRequest fetchRequestWithEntityName:#"Tag"];
NSExpressionDescription *countED = [NSExpressionDescription new];
countED.expression = [NSExpression expressionWithFormat:#"count:(items)"];
countED.name = #"countOfItems";
countED.expressionResultType = NSInteger64AttributeType;
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = #[#"tagName", countED];
NSArray *results = [self.context executeFetchRequest:fetch error:nil];
NSLog(#"results is %#", results);
which generates the following SQL:
SELECT t0.ZTAGNAME, (SELECT COUNT(t1.Z_3ITEMS) FROM Z_3TAGS t1 WHERE (t0.Z_PK = t1.Z_8TAGS) ) FROM ZTAG t0
Second attempt
Sadly, it seems CoreData gets confused if I try to select the objectID instead of the tagName:
NSExpressionDescription *selfED = [NSExpressionDescription new];
selfED.expression = [NSExpression expressionForEvaluatedObject];
selfED.name = #"self";
selfED.expressionResultType = NSObjectIDAttributeType;
fetch.resultType = NSDictionaryResultType;
fetch.propertiesToFetch = #[selfED, countED];
generates this SQL:
SELECT t0.Z_ENT, t0.Z_PK, COUNT( t1.Z_3ITEMS) FROM ZTAG t0 LEFT OUTER JOIN Z_3TAGS t1 ON t0.Z_PK = t1.Z_8TAGS
which counts all the rows from the outer join (and suggests that you need to group by the objectID, though we know that won't work).
Final attempt
However, include tagName and objectID, and all is well again:
fetch.propertiesToFetch = #[selfED, #"tagName", countED];
gives:
SELECT t0.Z_ENT, t0.Z_PK, t0.ZTAGNAME, (SELECT COUNT(t1.Z_3ITEMS) FROM Z_3TAGS t1 WHERE (t0.Z_PK = t1.Z_8TAGS) ) FROM ZTAG t0
which seems to do the trick. (Sorry for reverting to Objective-C, and for using different entity/attribute names, but I'm sure you get the picture).
Aside
One other curiosity I discovered is that the second attempt above can also be made to work by counting an attribute of the relationship, rather than the relationship itself:
countED.expression = [NSExpression expressionWithFormat:#"count:(items.itemName)"];
fetch.propertiesToFetch = #[selfED, countED];
gives:
SELECT t0.Z_ENT, t0.Z_PK, (SELECT COUNT(t2.ZITEMNAME) FROM Z_3TAGS t1 JOIN ZITEMS t2 ON t1.Z_3ITEMS = t2.Z_PK WHERE (t0.Z_PK = t1.Z_8TAGS) ) FROM ZTAG t0
which will (I think) give the correct counts provided itemName is not nil.
I played with this for a bit, sure there had to be some way to tell core-data to group by the primary key.
I couldn't figure it out, though I believe it to be possible.
The best I could do was add another unique attribute "uuid" (which I use for all of my entities anyway, for various reasons). You can do this easily enough with NSUUID, or you can take the permanent object ID URI representation and turn it into a string.
Anyway, I think this gives you what you want, but does so by requiring a separate unique attribute.
fetchRequest.propertiesToGroupBy = #[#"uuid"];
I tried a bunch of alternatives as the group-by property but expressionForEvaluatedObject always barfs, and other attempts fell flat.
I'm sure you know this already. Just in case, though it's at least a workaround, even if you don't use it for anything else, until someone comes around who has actually done this before.
FWIW, here is the SQL...
CoreData: sql: SELECT t0.Z_ENT, t0.Z_PK, COUNT( t1.Z_1ENTRIES), t0.ZUUID
FROM ZTAG t0 LEFT OUTER JOIN Z_1TAGS t1 ON t0.Z_PK = t1.Z_2TAGS
GROUP BY t0.ZUUID
Surely, there has to be a way to tell it to substitute t0.Z_PK in the group-by clause. I would image that should be an easy special case for expressionForEvaluatedObject or "objectID" or "self" or "self.objectID"
Good luck, and please report back if you solve it. I'd be very interested.
It is perhaps easier to use a NSFetchedResultsController. You can set the sectionNameKeyPath to group and use the resulting NSIndexPaths to construct your dictionary.
That being said, I do not think that it makes any sense to group by objectID because every object ID is by definition unique. So there will be one instance in each group. This is likely why setting propertiesToGroupBy fails.
So, short answer: no.
E.g.
let fetchRequest = NSFetchRequest(entityName: "Tag")
var output = [(NSManagedObjectID, Int)]()
do {
let results = try context.executeFetchRequest(request) as! [Tag]
for tag in results {
output.append((tag.objectID, tag.entries.count))
}
} catch {}
// output contains tuples with objectID and count
If entriesis optional, use tag.entries?.count ?? 0.

linq to entity Contains() and nested queries

i've a trouble with linq, i'll explain on example :
i have a database table called Employee which got FirstName and LastName columns,
and a method to search employees which gets a nameList list as argument, the elements in this list are names formatted like this one "Fred Burn", or this1 "Grim Reaper",
already tryed these approaches with no luck =[
//just all employees
var allData = from emp in Context.Employee select emp;
var test1 = from emp in allData
where(emp.FirstName + " " + emp.LastName).Contains
("" + ((from n in nameList select n).FirstOrDefault()))
select emp;
var test2 = (from emp in allData
where (emp.FirstName + " " + emp.LastName)
== ((from n in nameList select n).FirstOrDefault())
select emp);
var test3 = from emp in allData
where (from n in nameList select n).Contains
(emp.FirstName + " " + emp.LastName)
select emp;
first and second queries give : {"Unable to create a constant value of type 'Closure type'. Only primitive types ('such as Int32, String, and Guid') are supported in this context."} exceptionand third : {"LINQ to Entities does not recognize the method 'Boolean Contains[String](System.Collections.Generic.IEnumerable`1[System.String], System.String)' method, and this method cannot be translated into a store expression."}
would be glad to hear your suggestions :)
Thank You!
p.s.
yea i know it's possible to split names in list and compare them separately, but still curious why wont these queries work.
I assume nameList in this case is an in memory collection and that you are trying to use the LINQ to SQL trick creating a SQL "IN" clause inside of the Entity Framework. Unfortunately, EF doesn't support this syntax (yet). Depending on the number of records you are trying to match, you may be able to run separate queries for each child you are desiring. Alternatively, you could build an entitysql query using concatenation to append the multiple items from the nameList as separate OR clauses in the WHERE operation.

Resources