question

Craig DIckson avatar image
2 Likes"
Craig DIckson asked anthony.johnson edited

Query a table based on token labels in a list

I have a global table of SKUs with columns for aggregate demand and some other information (table is named "Status_SKUs", 10x20,000); There is also a list of tote tokens contianing those SKUs ("listInventory"), partitioned by storage location, 1-30 (i.e. machine).

The token representing each machine needs to find the SKU with the most demand that also has a tote in its partition of the list.

Any suggestions on what the query() statement executed by the machine would look like?

Thanks,

Craig

FlexSim 16.0.1
query
5 |100000

Up to 12 attachments (including images) can be used with a maximum of 23.8 MiB each and 47.7 MiB total.

1 Answer

anthony.johnson avatar image
3 Likes"
anthony.johnson answered anthony.johnson edited

Unfortunately the query command can't automatically search a partitioned list. It can only search an unpartitioned global list. If it where an unpartitioned global list, the query would look something like:

query("SELECT SKU, demand FROM Status_SKUs, listInventory WHERE Status_SKUs.SKU = listInventory.SKU ORDER BY demand DESC");

I'd like to add some syntax to the query command where you could do something like listInventory@partitionID to define a specific partition to search. For now, though, you'll have to use the $ syntax to do it manually. This would be something like:

query("SELECT SKU, demand FROM Status_SKUs, $1 AS Inv WHERE Inv.$2 = SKU ORDER BY demand DESC", 
    listentries(globallist("listInventory"), partitionID), 
    getlabel(tonode(getnodenum($iter(1))), "SKU"));

Here's how it works. I want to dupe the query command into thinking I actually have a table when in reality, it may be just some model data somewhere that I can access. So, I tell the query command I've got a "table" designated by $1. Putting $1 in the FROM clause means I'm going to tell the query what the table is in the first additional parameter to the query command. So, as my first additional parameter, I put: listentries(globallist("listInventory"), partitionID). This is a reference to a node in the tree containing all the current entries in the list partition. Now, from query's point of view, at this point all it needs to know is the number of rows in that table. By the fact that I return a reference to a node, it will assume that the number of rows in the "table" are the number of subnodes of that node, or, the number of entries currently in that partition of the list.

The query command then goes through my $1 "table", and whenever it needs a value for a "row", it makes $iter(1) return the subnode of the list entries that is associated with the row that it currently wants a value on. Thus $iter(1) is the "iteration on $1", or in this case it will reference a specific entry node in the list. Given an entry node, since in our case the entry value is a reference to a token, I can get the token with tonode(getnodenum($iter(1))). Further, if I want to get a label on that token, I can use getlabel(tonode(getnodenum($iter(1))), "SKU"). That's what I put into $2, or the second additional parameter of the query command. The WHERE clause is WHERE Inv.$2 = SKU. I've aliased $1 as Inv, and Inv.$2 designates that $2 is part of the $1 (or Inv) table. Thus I can put the getlabel() expression into the second parameter, and it will call that whenever it needs to compare the SKU of the global table with my token's SKU label (or Inv.$2).

I realize that having read the last two paragraphs you now might be more confused than when you started. I'm attaching an example model that essentially does what I'm talking about, so hopefully that may help. It is a basic model where I just added a bunch of objects to a list and then had SKU labels on those objects, and then inner join on the list and the table. The script window at the bottom does the query and dumps the result to a table.

In setting this up I realized that the listentries() command syntax checker disallows an object reference as the partition ID, which is a bug. So I put it on partition 1.

This answer tells you how to inner join with a single partition of a list. If you want to inner join with all partitions of a list, another option is to use the $1x$2 table definition syntax, which is another layer in the $ syntax. So if you need that, let me know and I can extend the answer.

partitionedlisttableinnerjoin.fsm


· 6
5 |100000

Up to 12 attachments (including images) can be used with a maximum of 23.8 MiB each and 47.7 MiB total.

Cameron Pluim avatar image Cameron Pluim commented ·

@Anthony,

I added another row to the table "Status_SKUs" in your example and gave it an SKU of 5, and a demand of 1, and in the QueryDump it is not pulling the obj. However, if the demand is >= 2 for this last row, the obj correctly shows /SKU5.

1090-partitionedlisttableinnerjoin-1.fsm

0 Likes 0 ·
anthony.johnson avatar image anthony.johnson ♦♦ Cameron Pluim commented ·

OK, I got a work-around. The bug is only caused when using $1 as a table. So if you make the table reference $2 (and switch the getlabel() call from $2 back to $1), it will work. So the query in the model would be:

query("SELECT SKU, demand, Inv.$3 AS obj FROM Status_SKUs, $2 AS Inv WHERE Inv.$1 = SKU ORDER BY demand", 
     getlabel(tonode(getnodenum($iter(2))), "SKU"), 
     listentries(globallist("ListInventory"), 1), 
     tonode(getnodenum($iter(2))));

1 Like 1 ·
Craig DIckson avatar image Craig DIckson anthony.johnson ♦♦ commented ·

I don't seem to getting any results. I confirmed that there is a SKU in the list which also has demand in the table. I'm wondering if it is related to my list being a default process flow list, not a global one (though it does say type "global" on the form)

snip-4.png

Here is my code:

/**Custom Code*/
treenode current = param(1);
treenode activity = param(2);
treenode token = param(3);
treenode processFlow = ownerobject(activity);

vPartitionShuttleNum = getlabel(token,"labelStorageLocation");

query("SELECT SKU, Demand, Inv.$3 AS obj FROM Status_SKUs , $2 AS Inv WHERE Inv.$1 = SKU ORDER BY Demand DESC LIMIT 1", 
	getlabel(tonode(getnodenum($iter(2))),"labelSKU"),
	listentries(globallist("ListInventory") , 1),   
	tonode(getnodenum($iter(2))));
dumpquery(reftable("QueryDump"), 1); 


pt("Shuttle "); pd(vPartitionShuttleNum); pt(" found SKU "); pd(getquerymatchcount()); pr();

0 Likes 0 ·
snip-4.png (9.8 KiB)
Show more comments
anthony.johnson avatar image anthony.johnson ♦♦ Cameron Pluim commented ·

Dang, that's a bug. We'll get that fixed in the next bug fix release. Unfortunately at this point I can't think of a great work-around for it. It seems like there's an issue where, for the first result row, if the row associated with a given SKU is the same in the status table as the list table, then it doesn't calculate the result properly. Weird correspondence there. So, I guess, do what you can until we get a fix.

0 Likes 0 ·
Cameron Pluim avatar image Cameron Pluim anthony.johnson ♦♦ commented ·

I don't actually have a model that I'm using this in right now, but I was just trying to understand $iter and how that worked (which this example explains well), when I decided to play around with it and discovered this. So hopefully this doesn't cause problems with other users models.

0 Likes 0 ·