Filter portal for most recently created record by group - filemaker

I have a portal on my "Clients" table. The related table contains the results of surveys that are updated over time. For each combination of client and category (a field in the related table), I only want the portal to display the most recently collected row.
Here is a link to a trivial example that illustrates the issue I'm trying to address. I have two tables in this example (Related on ClientID):
Clients
Table 1 Get Summary Method
The Table 1 Get Summary Method table looks like this:
Where:
MaxDate is a summary field = Maximum of Date
MaxDateGroup is a calculated field = GetSummary ( MaxDate ;
ClientIDCategory )
ShowInPortal = If ( Date = MaxDateGroup ; 1 ; 0 )
The table is sorted on ClientIDCategory
Issue 1 that I'm stumped on: .
ShowInPortal should equal 1 in row 3 (PKTable01 = 5), row 4 (PKTable01 = 6), and row 6 (PKTable01 = 4) in the table above. I'm not sure why FM is interpreting 1Red and 1Blue as the same category, or perhaps I'm just misunderstanding what the GetSummary function does.
The Clients table looks like this:
Where:
The portal records are sorted on ClientIDCategory
Issue 2 that I'm stumped on:
I only want rows with a ShowInPortal value equal to 1 should appear in the portal. I tried creating a portal filter with the following formula: Table 1 Get Summary Method::ShowInPortal = 1. However, using that filter removes all row from the portal.
Any help is greatly appreciated.

One solution is to use ExecuteSQL to grab the Max Date. This removes the need for Summary functions and sorts, and works as expected. Propose to return it as number to avoid any issues with date formats.
GetAsTimestamp (
ExecuteSQL (
"SELECT DISTINCT COALESCE(MaxDate,'')
FROM Survey
WHERE ClientIDCategory = ? "
; "" ; "";ClientIDCategory )
)
Also, you need to change the ShowInPortal field to an unstored calc field with:
If ( GetAsNumber(Date) = MaxDateGroupSQL ; 1 ; 0 )
Then filter the portal on this field.
I can send you the sample file if you want.

Related

How to query a reference table for a value between a dates for a specified category in Apps Script?

I have a background in data analytics and have done a similar workflow in SQL but am brand new to Apps Script. I am a bit at a loss on where to even start in Apps Script. Any advice or pointing me in the direction of useful examples would be truly appreciated!
Currently, I have a reference table on one sheet with categories and values and the start and end date that value applies to. Then I have a data table on another sheet where I add an entry date and a category and I would like to have Apps Script write the corresponding value for that category on the date.
Reference table data (a blank end date means that is the current rate):
Category
Value
Start date
End date
A
25
01/01/2022
3/31/2022
B
40
01/01/2022
C
30
01/01/2022
A
15
04/01/2022
The data table where the entry date and the category are added manually over time. I want to use the reference table to write the value for that category for that entry date.
Entry Date
Category
Value
02/20/2022
B
40
02/27/2022
A
25
03/20/2022
A
25
04/16/2022
C
15
05/12/2022
A
30
06/02/2022
B
40
How do you get the query the reference data for that entry date and category to find the row with the corresponding value?
Description
As I said I'm not good at QUERY but I finally got something to work. I'm sure other can improve on it.
First I created a named range TestQuery for the table of data. I could have just as easily used range "A1:D6"
Next I fill in the End Date with =TODAY() so it has a date value. Then I build my query.
=QUERY(TestQuery,"select B where ( ( A = '"&B11&"' ) and ( date '"&TEXT(A11,"yyyy-mm-dd")&"' > C ) and ( date '"&TEXT(A11,"yyyy-mm-dd")&"' < D ) )")
Reference
Query Language
Compare Dates in Query
Getting data from a table on a sheet
function getData() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("Sheet0");
const values = sh.getRange("A2:D" + sh.getLastRow()).getValues();
Logger.log(JSON.stringify(values));//2d array
}
A2 is assumed to be the upper left corner of the data

How to query the first row efficiently?

I have a table with large amount of records:
date instrument price
2019.03.07 X 1.1
2019.03.07 X 1.0
2019.03.07 X 1.2
...
When I query for the day opening price, I use:
1 sublist select from prices where date = 2019.03.07, instrument = `X
It takes a long time to execute because it selects all the prices on that day and get the first one.
I also tried:
select from prices where date = 2019.03.07, instrument = `X, i = 0 //It does not return any record (why?)
select from prices where date = 2019.03.07, instrument = `X, i = first i //Seem to work. Does it?
In Oracle an equivalent will be:
select * from prices where date = to_date(...) and instrument = "X" and rownum = 1
and Oracle will stop immediately when it finds the first record.
How to do this in KDB (e.g. stop immediately after it finds the first record)?
In kdb, where subclauses in select statements are executed sequentially. i.e. only those records which pass the first "test" get passed to the second test. With that in mind, looking at your two attempts:
select from prices where date = 2019.03.07, instrument = `X, i = 0 //It does not return any record (why?)
This doesn't (necessarily) return anything, because by the time it gets to the i=0 check, you've already filtered out some records (possibly including the first record in the original table, which would have i=0)
select from prices where date = 2019.03.07, instrument = `X, i = first i //Seem to work. Does it?
This one should work. First you filter by date. Then within the records for that date, you select the records for instrument `X. Then within those records, you take the record where i is the first i (where i has already been filtered down, so first i is simply the index of the first record [still the index from the original table, not the filtered down version])
Q-SQL equivalent for that is select[n] which also performs better than other approaches in most of the cases. Positive 'n' will give first n records and negative will give last n records.
q) select[1] from prices where date = 2019.03.07, instrument = `X
There is no inbuilt functionality to stop after first match. You can write custom function for that but that would probably execute slower than above supported version.

How to add blank lines to complete an Access report so that it matches a printed form?

I deal with a bunch of government forms, and I constantly find myself trying to get Access 2013 to output a report that matches a pre-printed form.
Most of the forms are now in .pdf form, so Access doesn't handle them well (to my knowledge) without additional software, which I cannot install on user production computers.
So I usually re-create the form as an Access report, but I have real difficulty when I have only enough records for a half a page.
How do I get a report to print the required records, and then fill the page with blank records so the "form" looks correct?
I'd be willing to try any possible solution - I've even gone so far as to create blank records to try to make this work. My goal, however, is to automate this process so any user can generate the report and it prints out correctly without a bunch of fiddling.
On this form, some or all of the lines might be used, and each person (I have ~550 people who each have an individual form) has a different number of lines, depending on the number of jumps they have completed.
I would have a dummy table with just a single numeric field called id. Populate this with a number of records greater than the biggest number of 'extra' records you're ever going to need to fill your form, using numbers from 1 upwards for id. Now say your original record source query for the report is:
select field1, field2, field3, field4
from myTable
order by field1
and you always want 15 rows to fill your form, then change the report's record source to:
select top 15 sort_seq, field1, field2, field3, field4
from (
select 0 as sort_seq, field1, field2, field3, field4
from myTable
union
select id, null, null, null, null
from dummyTable
) as X
order by sort_seq, field1
Ok, so you are recreating the complete PDF form as Access report. And the lower part is the details section of a continuous report.
The easiest way is indeed to create the correct number of empty records, but do this automatically (e.g. using DAO Recordset.AddNew).
I like to use a separate dummy table for this (with the same structure as the original table) to not pollute the original table with dummy records.
The form has 20 lines, so if your original table has 7 records for the current person, insert 13 records into the dummy table, using a large PK so they get sorted to the bottom.
Then use a query as record source for the report, joining the master table with a UNION of Jumps and Dummy table.
Alternatively there is the Report.Line() method: https://msdn.microsoft.com/en-us/library/office/ff198297.aspx
But this will be a lot more complicated to get it right.
I hope you have found the solution to this issue. I am posting this in the hope that if someone else finds this same question they will find this one as the best possible solution, as I have been struggling for about 2 days now.
I suppose you have at least two tables and a query where you mix information from both, and your report is made out from this query. I will call this your Query1, so the trick is that when you open the Report from your Form, you will use the label number of your Form to make the filter of the Query1 from where you will get the information for your report. Like this:
rectotal = DCount("*", "Quote-Report", "[Quote_No]=" & stQtNo)
This instruction will count how many records you have on your “Quote-Report” Query that are of the same value of the [Quote_No] field inside your Quote Form , as you are assigning the stQtNo variable to the Form with the name “Quote”; and put this value on your rectotal variable.
Now with rectotal you need to determine how many lines you want in your report. I used 28 so the following formula will give how many blank rows needed to fill the page.
If rectotal / 28 - Int(rectotal / 28) = 0 Then
x = 28
Else
x = 28 * (rectotal / 28 - Int(rectotal / 28))
End If
blanklines = 28 - x
tLines = rectotal + blanklines
Of course you can use 15, 20 or as many rows you want your report to print per page. The variable blanklines will give you this number.
Then I use the recordset procedure from Access to open the table where I have the information that will print in the detail section of the report and fill with as many records as I need using the blanklines variable and the Do Until Loop.
Set db = CurrentDb
Set rs = db.OpenRecordset("Item_QD", dbOpenDynaset, dbseechanges)
Item_QD is the table
where I want to insert new records to fill the report detail section.
x = 0
rs.MoveLast
Do Until x = blanklines
rs.AddNew
rs![Quote_No] = stQtNo
rs![CODE No] = "Z" & x I use the Z and the number so I can erase the records once I don’t need them anymore.
x = x + 1
rs.Update
Loop
Please note that you need to Dim db As Database and Dim rs As Recordset variables outside your private sub in module header. Also your Access needs to be running these two classes so you can use them.
Finally once you open your report you have to go to your detail section and do a requery command to refresh all the rows prior to print. Need to format your detail cells in order to print the grid line and the rest is history.
The beauty of this procedure is that you can add and handle as many rows as you want or need inside your report and make a professional look of your quotes, packing slips, purchase orders, etc.
Hope this solves many problems. For a fast reference on how to use and manage ACCESS record set visit: https://www.youtube.com/watch?v=WNm17l54z1c
Private Sub Report_Open(Cancel As Integer)
Dim blanklines, tLines As Integer
Dim rectotal, x As Long
Dim stQtNo As String
Set db = CurrentDb
Set rs = db.OpenRecordset("Item_QD", dbOpenDynaset, dbseechanges)
stQtNo = Forms![Quote].[Quote_No]
rectotal = DCount("*", "Quote-Report", "[Quote_No]=" & stQtNo)
If rectotal / 28 - Int(rectotal / 28) = 0 Then
x = 28
Else
x = 28 * (rectotal / 28 - Int(rectotal / 28))
End If
blanklines = 28 - x
tLines = rectotal + blanklines
x = 0
rs.MoveLast
Do Until x = blanklines
rs.AddNew
rs![Quote_No] = stQtNo
rs![CODE No] = "Z" & x
x = x + 1
rs.Update
Loop
End Sub
Another solution. Code behind report. I use it for a simple report without grouping and row height doesn't change.
Option Compare Database
Option Explicit
Public totalCount As Integer
Private Sub Detail_Print(Cancel As Integer, PrintCount As Integer)
printBlankRecords getnumberofBlanks
End Sub
Public Sub printBlankRecords(numberBlanks As Integer)
Dim recordCount As Integer
recordCount = DCount("*", Me.RecordSource)
totalCount = totalCount + 1
If totalCount = recordCount Then
Me.NextRecord = False
'once you get to the last record, stay on last record
ElseIf totalCount > recordCount And totalCount < (recordCount + numberBlanks) Then
Me.NextRecord = False
'make the font and backcolor the same appearing to be empty record for each field
Me.RateID.ForeColor = Me.RateID.BackColor
End If
End Sub
Public Function getnumberofBlanks() As Integer
Dim recordCount As Integer
Dim N As Integer, X As Integer
X = 20
recordCount = DCount("*", Me.RecordSource)
If recordCount <= X Then
getnumberofBlanks = X - recordCount
Else
N = 1
Do While N * 21 + X <= recordCount
N = N + 1
Loop
getnumberofBlanks = (N * 21 + X) - recordCount
End If
End Function

Min value with GROUP BY in Power BI Desktop

id datetime new_column datetime_rankx
1 12.01.2015 18:10:10 12.01.2015 18:10:10 1
2 03.12.2014 14:44:57 03.12.2014 14:44:57 1
2 21.11.2015 11:11:11 03.12.2014 14:44:57 2
3 01.01.2011 12:12:12 01.01.2011 12:12:12 1
3 02.02.2012 13:13:13 01.01.2011 12:12:12 2
3 03.03.2013 14:14:14 01.01.2011 12:12:12 3
I want to make new column, which will have minimum datetime value for each row in group by id.
How could I do it in Power BI desktop using DAX query?
Use this expression:
NewColumn =
CALCULATE(
MIN(
Table[datetime]),
FILTER(Table,Table[id]=EARLIER(Table[id])
)
)
In Power BI using a table with your data it will produce this:
UPDATE: Explanation and EARLIER function usage.
Basically, EARLIER function will give you access to values of different row context.
When you use CALCULATE function it creates a row context of the whole table, theoretically it iterates over every table row. The same happens when you use FILTER function it will iterate on the whole table and evaluate every row against the filter condition.
So far we have two row contexts, the row context created by CALCULATE and the row context created by FILTER. Note FILTER use the EARLIER to get access to the CALCULATE's row context. Having said that, in our case for every row in the outer (CALCULATE's row context) the FILTER returns a set of rows that correspond to the current id in the outer context.
If you have a programming background it could give you some sense. It is similar to a nested loop.
Hope this Python code points the main idea behind this:
outer_context = ['row1','row2','row3','row4']
inner_context = ['row1','row2','row3','row4']
for outer_row in outer_context:
for inner_row in inner_context:
if inner_row == outer_row: #this line is what the FILTER and EARLIER do
#Calculate the min datetime using the filtered rows
...
...
UPDATE 2: Adding a ranking column.
To get the desired rank you can use this expression:
RankColumn =
RANKX(
CALCULATETABLE(Table,ALLEXCEPT(Table,Table[id]))
,Table[datetime]
,Hoja1[datetime]
,1
)
This is the table with the rank column:
Let me know if this helps.

Calculate value based on existence of records matching given criteria - FileMaker Pro 13

How can I write a calculation field in a table that outputs '1' if there are other (related) records in the same table that meet a given set of criteria and '0' otherwise?
Here's my problem explained in more detail:
I have a table containing 'students' and another containing 'exam results'. The 'exam results' table looks like this:
StudentID SubjectID Level Result
3234 1 2 A-
3234 2 4 B+
4739 1 4 C+
A student can only pass a Level 4 exam in subject 2 if they have also passed a Level 2 exam in subject 1 with a B+ or higher. I want to define a field in the 'students' table that contains a '1' if there exists an exam result belonging to the right student that meets these criteria and a '0' otherwise.
What would be the best way to do this?
Let us take an example of a Results table where the results are also calculated as a numeric value, e.g.
StudentID SubjectID Level Result cResultNum
3234 1 2 A- 95
3234 2 4 B+ 85
4739 1 4 C+ 75
and an Exams table with the following fields (among others):
RequiredSubjectID
RequiredLevel
RequiredResultNum
Given these, you can construct a relationship between Exams and (another occurrence of) Results as:
Exams::RequiredSubjectID = Results 2::SubjectID
AND
Exams::RequiredLevel = Results 2::Level
AND
Exams::RequiredResultNum ≤ Results 2::cResultNum
This allows each exam record to calculate a list of students that are eligible to take that exam as =
List ( Results 2::StudentID )
I want to define a field in the 'students' table that contains a '1'
if there exists an exam result belonging to the right student that
meets these criteria and a '0' otherwise.
This request is unclear, because there are many exams a student may want to take, and a field in the Students table can calculate only one result.
You need to do a self-join in the table for the field you want to check, for example:
Exam::Level = Exam2::Level
Exam::Student = Exam2::Student
And for the "was passed" criteria I think you could do an "If" on the calculation like this:
If ( Last(Exam2::Result) = "D" and ...(all the pass values) ; 1 ; 0 )
Edit:
It could be just with the not pass value hehe I miss that it will be like this:
If ( Last(Exam2::Result) = "F" ; 0 ; 1 )
I hope this helps you.