DAX - Summary table for Planned and Executed production - join

I have a problem witn joining two tables (tbPlan and tbExecuted).
The tbPlan is like this
Date
SKU
Shift
Qty
1/1/2023
A
1
10
1/1/2023
A
2
5
1/1/2023
B
1
10
and the tbExecuted is like:
Date
SKU
Shift
Qty
1/1/2023
A
1
10
2/1/2023
A
1
5
1/1/2023
B
1
5
1/1/2023
B
2
5
The join table must have all the information for planning and executed like this:
Date
SKU
Shift
Plan
Executed
1/1/2023
A
1
10
10
1/1/2023
A
2
5
0
2/1/2023
A
1
0
5
1/1/2023
B
1
10
5
1/1/2023
B
2
0
5
I tried to use this DAX code (real names of tables and columns)
FACT-TabelaAderencia =
VAR A =
SELECTCOLUMNS(
summProducao
;"Data"; summProducao[DataProducao]+0
;"SKU"; Upper(summProducao[SKU])
;"Turno"; summProducao[Turno]*1
;"Produzido"; summProducao[Cargas Produzidas]
)
VAR B =
SELECTCOLUMNS(
summPlanejamento
;"Data"; summPlanejamento[DataProgramada1]+0
;"SKU"; UPPER(summPlanejamento[SKU])
;"Turno"; summPlanejamento[Turno]*1
;"Planejado"; summPlanejamento[Planejadas]
)
VAR Result = NATURALLEFTOUTERJOIN(A; B)
Return Result
The NATURALLEFTOUTERJOIN show only the 0s on the first argument of the function (if I put tbPlan, none row with 0 executed is shown). And vice-versa.
The NATURALINNERJOIN kills both rows with 0 in tbPlan or tbExecuted.
Is there a way to achieve this goal of having a summary of planning and executed in one summary table?
I appreciate any help.

NATURALLEFTOUTERJOIN is always a left join. So, you have left table and get it with some appended values.
You can't reach the goal with a join, but you can use some other technics. I used UNION() and GROUPBY(), but there are still other ways to get the same output.
VAR planFact =
UNION(
SELECTCOLUMNS(
plan
,"Data", plan[Date]+0
,"SKU", UPPER(plan[SKU])
,"Turno", plan[Shift]*1
,"Produzido",VALUE("0")
,"Planejado", plan[pQty]
)
,
SELECTCOLUMNS(
'fact'
,"Data", 'fact'[Date]+0
,"SKU", UPPER('fact'[SKU])
,"Turno", 'fact'[Shift]*1
,"Produzido", 'fact'[fQty]
,"Planejado",VALUE("0")
)
)
RETURN
GROUPBY(
planFact
,[Data]
,[SKU]
,[Turno]
,"Planejado",SUMX(CURRENTGROUP (),[Planejado])
,"Produzido",SUMX(CURRENTGROUP (),[Produzido])
)

Merging tables is really not something you should be doing using DAX. However, you can do this in Power Query with a full join and some null-coalesing operators. Here is some (really ugly) code:
tbPlan:
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMtQ31DcyMDJW0lFyBGJDEDZQitXBkDECYlN0CSckLbEA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Date = _t, SKU = _t, Shift = _t, Qty = _t]),
#"Changed column type" = Table.TransformColumnTypes(Source, {{"Date", type date}, {"SKU", type text}, {"Shift", Int64.Type}, {"Qty", Int64.Type}})
in
#"Changed column type"
tbExecuted:
let
Source = Table.FromRows(Json.Document(Binary.Decompress(Binary.FromText("i45WMtQ31DcyMDJW0lFyBGJDEDZQitWJVjLClDEFSyBpccInYQSRiAUA", BinaryEncoding.Base64), Compression.Deflate)), let _t = ((type nullable text) meta [Serialized.Text = true]) in type table [Date = _t, SKU = _t, Shift = _t, Qty = _t]),
#"Changed column type" = Table.TransformColumnTypes(Source, {{"Date", type date}, {"SKU", type text}, {"Shift", Int64.Type}, {"Qty", Int64.Type}})
in
#"Changed column type"
And the merged table code:
let
Source = Table.NestedJoin(tbPlan, {"Date", "SKU", "Shift"}, tbExecuted, {"Date", "SKU", "Shift"}, "tbExecuted", JoinKind.FullOuter),
#"Expanded tbExecuted" = Table.ExpandTableColumn(Source, "tbExecuted", {"Date", "SKU", "Shift", "Qty"}, {"Date.1", "SKU.1", "Shift.1", "Qty.1"}),
#"Added custom" = Table.AddColumn(#"Expanded tbExecuted", "DateTemp", each [Date] ?? [Date.1]),
#"Added custom 1" = Table.AddColumn(#"Added custom", "SKUTemp", each [SKU] ?? [SKU.1]),
#"Added custom 2" = Table.AddColumn(#"Added custom 1", "ShiftTemp", each [Shift] ?? [Shift.1]),
#"Added custom 3" = Table.AddColumn(#"Added custom 2", "Qty Plan", each [Qty] ?? 0),
#"Added custom 4" = Table.AddColumn(#"Added custom 3", "Qty Executed", each [Qty.1] ?? 0),
#"Removed other columns" = Table.SelectColumns(#"Added custom 4", {"DateTemp", "SKUTemp", "ShiftTemp", "Qty Plan", "Qty Executed"}),
#"Renamed columns" = Table.RenameColumns(#"Removed other columns", {{"DateTemp", "Date"}, {"SKUTemp", "SKU"}, {"ShiftTemp", "Shift"}})
in
#"Renamed columns"
Result:

Related

Print the Lua table and connect the strings

I want to print the table, more complex. like this:
I don't know how to check each value in the table that equals and connects string like this (value1, value2, value3, and value4) Before it ends, it must end with and
Table:
table = {
{amount = 1, items = "item1"},
{amount = 1, items = "item2"},
{amount = 1, items = "item3"},
{amount = 2, items = "item4"},
{amount = 3, items = "item5"},
}
P.S I need to sort them too.
table.sort(table, function(a,b) return a.amount < b.amount end)
but I am still stuck with connect string that I mentioned above.
I want it to output like this:
3x item5
2x item4
1x item1, item2, and item3
Thanks for the answers, I newbie to Lua, and English is not my native language. Sorry for the incorrect grammar and words.
the solution is something like this, only without the output of and between item2, and item3 :
local t = {
{amount = 1, items = "item1"},
{amount = 1, items = "item2"},
{amount = 1, items = "item3"},
{amount = 2, items = "item4"},
{amount = 3, items = "item5"},
}
local res = {}
for k,v in pairs(t) do
res[v.amount] = res[v.amount] and (res[v.amount] .. ', ' .. v.items) or v.items -- use ternary operator
end
for k,v in pairs(res) do
v = v:gsub("(.*), (.-)","%1 and %2")
print(k,v)
end

Limiting query for one result where there is 2 values are matching

I am trying to query 2 long columns for agents' name, the issue is the names are repeated on 2 tables, one for the total sum of productivity and the other is for total sum of utilization.
The thing is when I query the columns it returns back the numbers for Productivity and Utilization all together.
How can I make the query to search only for Productivity alone and for Utilization alone?
Link is here: https://docs.google.com/spreadsheets/d/12Sydw6ejFobySHUj5JoYkAPbhr0mKoInCWxtHY1W4lk/edit#gid=0
Apps Script would be a better solution in this case. The code below works as follows:
Gets the names from Column D and Column A.
For each name of Column D, it will compare it with each name of Column A (that's the 2 for loops)
If the names coincide (first if), it will check the background color (second if) of the Column A name to accumulate Total Prod and Total Util.
Once it reaches the end of the Column A, writes the values in Total Prod and Total Util (Columns E and F) for each name in D.
function onOpen() { //Will run every time you open the sheet
//Gets the active Spreadsheet and sheet
let sprsheet = SpreadsheetApp.getActiveSpreadsheet();
let sheet = sprsheet.getActiveSheet();
var lastRow = sheet.getLastRow();
var getNames = sheet.getRange(3, 1, lastRow).getValues(); //Names from row 2, col 1, until the last row
var totalNames = sheet.getRange("D4:D5").getValues(); //Change the range for more names
let prodColor = '#f2f4f7'; //hexadecimal codes of the background colors of names in A
let utilColor = '#cfe2f3'; //
for (var i = 0; i < totalNames.length; i++) {
var totalProd = 0, totalUtil = 0; //Starts at 0 for each name in D
for (var j = 0; j < getNames.length; j++) {
if (totalNames[i][0] == getNames[j][0]) {
if (sheet.getRange(j + 3, 1).getBackgroundObject().asRgbColor().asHexString() == prodColor) { //if colors coincide
totalProd += sheet.getRange(j + 3, 2).getValue();
} else if (sheet.getRange(j + 3, 1).getBackgroundObject().asRgbColor().asHexString() == utilColor) {
totalUtil += sheet.getRange(j + 3, 2).getValue();
}
}
}
sheet.getRange(i+4, 5, 1 ,2).setValues([[totalProd, totalUtil]]);
}
}
Note: You will have to run the code manually and accept permissions the first time you run it. After that it will run automatically each time you open the Sheet. It might take a few seconds for the code to run and to reflect changes on the Sheet.
To better understand loops and 2D arrays, I recommend you to take a look at this.
References:
Range Class
Get Values
Get BackgroundObject
Set Values
You can learn more about Apps Script and Sheets by following the Quickstart.

Google Sheet If dropdown

I have drop down for column A where I want to select value from drop down only for certain times (ie. 3 times), after I select value from dropdown 3 times, if I try to select it 4th time, value should be removed from dropdown or not able to select. Is this possible using excel or google sheet?
https://docs.google.com/spreadsheets/d/1nbXAkK565V24KDTAzE68q8rQgQWzn-jDJz_6piNYyEw/edit?usp=sharing
In above google sheet, I had selected Red 3 times, now if I want to select Red 4th time, I should not be able to select or Red should be removed from list.
I know using excel VBA, I can do same using below code, but can we add same to google sheets?
Option Explicit
Private Sub Worksheet_Change(ByVal Target As Range)
Dim rngDV As Range
Dim oldVal As String
Dim newVal As String
Dim lVal As Long
Dim check2 As Long
If Target.Count > 2 Then GoTo exitHandler
On Error Resume Next
Set rngDV = Cells.SpecialCells(xlCellTypeAllValidation)
On Error GoTo exitHandler
If rngDV Is Nothing Then GoTo exitHandler
If Intersect(Target, rngDV) Is Nothing Then
'do nothing
Else
Application.EnableEvents = False
newVal = Target.Value
Application.Undo
oldVal = Target.Value
Target.Value = newVal
lVal = Application.WorksheetFunction _
.CountIf(Columns(Target.Column), _
"*" & newVal & "*")
If lVal > 3 Then
If newVal = "" Then
'do nothing
Else
MsgBox "Not available"
Target.Value = oldVal
End If
Else
If Target.Column >= 47 And Target.Column <= 56 Then
If oldVal = "" Then
'do nothing
Else
If newVal = "" Then
'do nothing
Else
Target.Value = oldVal _
& ", " & newVal
End If
End If
End If
End If
End If
exitHandler:
Application.EnableEvents = True
End Sub
So after doing some research I found the way of achieving what you were aiming for here.
Solution
In the class data validation it mentions a method to set the validation to null within the range class. You can check more details about this method here. In that method it mentions that if the parameter is set to null, it will disable the data validation (dropdowns) therefore not allowing to select other values.
Here is a piece of sample code self explained with comments to achieve what you want in your specific case:
function onEdit(){
// get the sheet and values to check if in that range there are more than 3 elements selected
var ss = SpreadsheetApp.getActiveSheet();
var values = ss.getRange('A1:A18').getValues();
// this variable is for counting the amount of elements selected
var count = 0;
for(i=0;i<values.flat().length;i++){
// if an element in that range is not empty
if(values.flat()[i]!=''){
count++;
}
}
// if the count is over 3 then disable the dropdowns
if(count>3){
ss.getRange('A1:A18').setDataValidation(null);
}
}
I hope this has helped you. Let me know if you need anything else or if you did not understood something. :)

Dynamically building subtables in a table

I'm trying to figure out how to dynamically build a series of sub-tables inside a lua table. For example
function BuildsubTable()
local oTable = {}
local name = {"P1","P2"}
for i = 1, 2 do
oTable[i] = {name = name[i], "x" = i + 2, "y" = i + 1}
end
return oTable
end
expected output:
oTable = {
{name = "P1", "x"=3, "y"=2},
{name = "P2", "x"=4, "y"=3}
}
Which obviously doesn't work, but you get the idea of what I'm trying to do. This is a rather simple task but in LUA 5.3 this is proving to be difficult. I cannot find a good example of building a table in this manner. Any solutions or other ideas would be appreciated. In Python I would use a class or a simple dictionary.
Your problem is that you quote the string indices. The generic syntax for declaring a table key inside the table constructor is [<key>] = <value>, for example, [20] = 20 or ["x"] = i + 2.
A shorthand for ["<key>"] = <value>, that is, for string indices that are valid variable names, you can write <key> = <value>, for example x = i + 2.
In your code you use a mix of both and write { ..., "x" = i + 2, ... }. A quick google search shows me that in Python, which you mention, you quote string keys in dictionaries, so you probably mixed that up with Lua?
EDIT: I noticed this a bit late, but you can also use ipairs to iterate the first table and table.insert to insert values:
function BuildsubTable()
local oTable = {}
local name = {"P1","P2"}
for i,name in ipairs(name) do
table.insert(oTable, {name = name, "x" = i + 2, "y" = i + 1})
end
return oTable
end
Use
oTable[i] = {name = name[i], x = i + 2, y = i + 1}
DarkWiiPlayers & lhf's answers are the proper way.
But here is how you can fix your current code if you intend to use a string as a key
function BuildsubTable()
local oTable = {}
local name = {"P1","P2"}
for i = 1, 2 do
oTable[i] = {name = name[i], ["x"] = i + 2, ["y"] = i + 1}
end
return oTable
end
Output
{
[1] = { ['name'] = 'P1', ['x'] = 3, ['y'] = 2},
[2] = { ['name'] = 'P2', ['x'] = 4, ['y'] = 3}
}

Open Office Spreadsheet (Calc) - Concatenate text cells with delimiters

I am using Open Office's spreadsheet program and am trying to concatenate several text cells together with delimeters. For example, suppose I have the cells below:
+--------+
| cell 1 |
+--------+
| cell 2 |
+--------+
| cell 3 |
+--------+
| cell 4 |
+--------+
| cell 5 |
+--------+
I would like to concatenate them with delimiters so that the result is in one cell like this one:
+----------------------------------------------+
| (cell 1),(cell 2),(cell 3),(cell 4),(cell 5) |
+----------------------------------------------+
My first thought was to try and make a macro or something, but I don't think open office supports those. Any ideas?
Thanks a lot Markus for finding a solution to this.
Here are some slightly more detailed instructions for the benefit of OpenOffice Basic newbies like myself. This applies to version 3.1:
Tools -> Macros -> Organize Macros -> OpenOffice.org Basic...
Now select from the explorer tree where you want your function live,
e.g. it can be in your own macro library (My Macros / Standard) or
stored directly in the current spreadsheet.
Now enter a new Macro name and click New to open the OO.org Basic IDE. You'll see a REM
statement and some stub Sub definitions. Delete all that and replace
it with:
Function STRJOIN(range, Optional delimiter As String, Optional before As String, Optional after As String)
Dim row, col As Integer
Dim result, cell As String
result = ""
If IsMissing(delimiter) Then
delimiter = ","
End If
If IsMissing(before) Then
before = ""
End If
If IsMissing(after) Then
after = ""
End If
If NOT IsMissing(range) Then
If NOT IsArray(range) Then
result = before & range & after
Else
For row = LBound(range, 1) To UBound(range, 1)
For col = LBound(range, 2) To UBound(range, 2)
cell = range(row, col)
If cell <> 0 AND Len(Trim(cell)) <> 0 Then
If result <> "" Then
result = result & delimiter
End If
result = result & before & range(row, col) & after
End If
Next
Next
End If
End If
STRJOIN = result
End Function
The above code has some slight improvements from Markus' original:
Doesn't start with a delimiter when the first cell in the range is empty.
Allows optional choice of the delimiter (defaults to ","), and the
strings which go before and after each non-blank entry in the range
(default to "").
I renamed it STRJOIN since "join" is the typical name of this
function in several popular languages, such as Perl, Python, and Ruby.
Variables all lowercase
Now save the macro, go to the cell where you want the join to appear,
and type:
=STRJOIN(C3:C50)
replacing C3:C50 with the range of strings you want to join.
To customise the delimiter, instead use something like:
=STRJOIN(C3:C50; " / ")
If you wanted to join a bunch of email addresses, you could use:
=STRJOIN(C3:C50; ", "; "<"; ">")
and the result would be something like
<foo#bar.com>, <baz#qux.org>, <another#email.address>, <and#so.on>
Well, after a lot more searching and experimenting, I found you can make your own functions in calc. This is a function I made that does what I want:
Function STRCONCAT(range)
Dim Row, Col As Integer
Dim Result As String
Dim Temp As String
Result = ""
Temp = ""
If NOT IsMissing(range) Then
If NOT IsArray(range) Then
Result = "(" & range & ")"
Else
For Row = LBound(range, 1) To UBound(range, 1)
For Col = LBound(range, 2) To UBound(range, 2)
Temp = range(Row, Col)
Temp = Trim(Temp)
If range(Row, Col) <> 0 AND Len(Temp) <> 0 Then
If(NOT (Row = 1 AND Col = 1)) Then Result = Result & ", "
Result = Result & "(" & range(Row, Col) & ") "
End If
Next
Next
End If
End If
STRCONCAT = Result
End Function
Ever so often I'd enjoy the ease and quickness of replace & calculation Options as well as in general the quick handling & modifying Options, when once again sitting in front of a dumped-file-lists or whatsoever.
I never understood why they didn't include such an essential function right from the start, really.
It's based on Adam's script, but with the extension to swap CONCAT from horizontal to vertical, while still keeping the delimiters in order.
Function CONCAT2D(Optional range, Optional delx As String, Optional dely As String, _
Optional xcell As String, Optional cellx As String, _
Optional swop As Integer)
Dim xy(1), xyi(1), s(1) As Integer
Dim out, cell, del, dxy(1) As String
'ReDim range(2, 1) 'Gen.RandomMatrix 4 Debugging
'For i = LBound(range, 1) To UBound(range, 1)
' For j = LBound(range, 2) To UBound(range, 2)
' Randomize
' range(i,j) = Int((100 * Rnd) )
' Next
'Next
out = ""
If IsMissing(delx) Then : delx = "," : End If
If IsMissing(dely) Then : dely = delx() : End If
If IsMissing(xcell) Then : xcell = "" : End If
If IsMissing(cellx) Then : cellx = xcell() : End If
If IsMissing(swop) Then : swop = 0 : End If
dxy(0) = delx() : dxy(1) = dely()
xyi(0) = 1 : xyi(1) = 2
If swop = 0 Then : s(0) = 0 : s(1) = 1
Else s(0) = 1 : s(1) = 0 : End If
If NOT IsMissing(range) Then
If NOT IsArray(range) _
Then : out = xcell & range & cellx
Else del = delx
For xy(s(0)) = LBound(range, xyi(s(0))) To UBound(range, xyi(s(0))
For xy(s(1)) = LBound(range, xyi(s(1))) To UBound(range, xyi(s(1))
cell = range(xy(0), xy(1))
If cell <> 0 AND Len(Trim(cell)) <> 0 _
Then : If out <> "" Then : out = out & del : End If
out = out & xcell & cell & cellx
del = dxy(s(0))
End If
Next : del = dxy(s(1))
Next
End If
Else out = "ERR"
End If
CONCAT2D = out
End Function

Resources