Can I create an Airtable view containing records not present in a "linked records" column? - filtering

I have a table with columns like:
Name (Single Line Text) | Partner (Link to "Name")
----------------------- | --------------------------
John Doe | `Jane Doe`
Jane Doe | (Empty)
Bob Smith | `Mary Smith` `Kevin Smith`
Mary Smith | (Empty)
Kevin Smith | (Empty)
Alan Stephens | (Empty)
I'd like to create a new view for this table with filtering like:
WHERE {Partner (field)} is not empty
OR {Partner (column)} does not contain {Name}
such that the result would be:
Name (Single Line Text) | Partner (Link to "Name")
----------------------- | --------------------------
John Doe | `Jane Doe`
Bob Smith | `Mary Smith` `Kevin Smith`
Alan Stephens | (Empty)
The challenge here is that on line 1 of my filter, I'm looking for "records with no records linked in their Partner column", but on line 2 I'm looking for "records which are not found in the Partner column for any other record".
The overall goal is to generate a list of records with no Partner(s) plus records who are not anyone else's Partner. Is there a way to achieve this?

I discovered that a filter such as this one is outside the available feature set of Airtable. I was able to implement a workaround:
Add a Script block containing the script described in this video (script gist)
Use the Script block to populate a new column on the table (Partner Of)
Name (Single Line Text) | Partner (Link to "Name") | Partner Of (Link to "Name")
----------------------- | -------------------------- | ---------------------------
John Doe | `Jane Doe` | (Empty)
Jane Doe | (Empty) | `John Doe`
Bob Smith | `Mary Smith` `Kevin Smith` | (Empty)
Mary Smith | (Empty) | `Bob Smith`
Kevin Smith | (Empty) | `Bob Smith`
Alan Stephens | (Empty) | (Empty)
Create a new view with a filter like:
WHERE {Partner Of} is empty

Related

Return rows which have the same values in two columns, but different values in another

I have a table that looks like this:
id | name | address | code
-----------+--------------------------+--------------------+----------
101 | joe smith | 1 long road | SC1
102 | joe smith | 6 long road | SC1
103 | amy hughes | 5 hillside lane | SC5
104 | amy hughes | 5 hillside lane | SC5
I want to return the rows that are duplications based on name and code but have different address fields.
I had something like this originally (which looked for duplications across the name, address and code columns:
SELECT name, address, code, count(*)
FROM table_name
GROUP BY 1,2,3
HAVING count(*) >1;
Is there a way I can expand on the above to only return rows that have the same name and code but different address fields?
In my example data above, I would only want to return:
id | name | address | code
-----------+--------------------------+--------------------+----------
101 | joe smith | 1 long road | SC1
102 | joe smith | 6 long road | SC1
Remove address from the select list and GROUP BY and use count(DISTINCT):
SELECT name, code, count(DISTINCT address)
FROM table_name
GROUP BY name, code
HAVING count(DISTINCT address) > 1;

Postgres GROUP BY an array column

I have a list of students and parents and would like to group them into families using the student id's. Parents who share common student id's can be considered to be a family while also students who share common parent id's can be considered to be a family. This is a sample table:
p_id | parent_name | s_id | student_name |
------------------------------------------|
1 | John Doe | 100 | Mike Doe |
3 | Jane Doe | 100 | Mike Doe |
3 | Jane Doe | 105 | Lisa Doe |
5 | Will Willy | 108 | William Son |
I'd like to end up with something like:
parents | students |
-------------------|------------------------|
John Doe, Jane Doe | Mike Doe, Lisa Doe |
Will Willy | William Son |
To achieve this I'm currently using:
SELECT array_agg(parents) AS parents FROM (
SELECT array_agg(p_id) AS par_ids, array_agg(parent_name) AS parents, student_name, s_id
FROM (
/* sub query */
)b
GROUP BY s_id, student_name
ORDER BY parents ASC
)c
GROUP BY unnest(par_ids)
ORDER BY parents ASC
But I get an error: ERROR: cannot accumulate arrays of different dimensionality. SQL state: 2202E
How can I attain the desired results?
The inner query from the above statement returns:
| par_ids | parents | student_name | s_id |
--------------------------------|------------------------|
| {1,3} | {John Doe, Jane Doe}| Mike Doe | 100 |
| {3} | {Jane Doe} | Lisa Doe | 105 |
| {5} | {Will Willy} | William Son | 108 |
Grouping these students now to the parents is where I'm stuck.
I did something similar (but a bit more complex) already here: https://stackoverflow.com/a/53129510/3984221
step-by-step demo:db<>fiddle
SELECT
array_agg(parent_name) as parents, -- 4
array_agg(student_name) as students
FROM (
SELECT DISTINCT ON (t.s_id) -- 3
*
FROM (
SELECT
s_id,
array_agg(p_id) as parents -- 1
FROM mytable
GROUP BY s_id
) s JOIN mytable t ON t.p_id = ANY(s.parents) -- 2
ORDER BY t.s_id, CARDINALITY(parents) DESC -- 3
) s
GROUP BY parents
Aggregate the p_id values into an array:
s_id
parents
108
{5}
105
{3}
100
{1,3}
Self-join the original table on this array:
s_id
parents
p_id
parent_name
s_id
student_name
100
{1,3}
1
John Doe
100
Mike Doe
105
{3}
3
Jane Doe
100
Mike Doe
100
{1,3}
3
Jane Doe
100
Mike Doe
105
{3}
3
Jane Doe
105
Lisa Doe
100
{1,3}
3
Jane Doe
105
Lisa Doe
108
{5}
5
Will Willy
108
William Son
Remove all duplicate student records. The remaining ones should be the records with the most complete p_id array. This can be done using DISTINCT ON(s_id) on a descending order by the array length:
s_id
parents
p_id
parent_name
s_id
student_name
100
{1,3}
1
John Doe
100
Mike Doe
100
{1,3}
3
Jane Doe
105
Lisa Doe
108
{5}
5
Will Willy
108
William Son
Finally you can group by the p_id array and aggregate the two name columns:
parents
students
{"John Doe","Jane Doe"}
{"Mike Doe","Lisa Doe"}
{"Will Willy"}
{"William Son"}
If you don't want to get an array, but a string list, you can use string_agg(name_colum, ',') instead of array_agg(name_column)

PostgreSQL COUNT DISTINCT on one column while checking duplicates of another column

I have a query that results in such a table:
guardian_id | child_id | guardian_name | relation | child_name |
------------|----------|---------------|----------|------------|
1 | 1 | John Doe | father | Doe Son |
2 | 1 | Jane Doe | mother | Doe Son |
3 | 2 | Peter Pan | father | Pan Dghter |
4 | 2 | Pet Pan | mother | Pan Dghter |
1 | 3 | John Doe | father | Doe Dghter |
2 | 3 | Jane Doe | mother | Doe Dghter |
So from these results, I need to count the families. That is, distinct children with the same guardians. From the results above, There are 3 children but 2 families. How can I achieve this?
If I do:
SELECT COUNT(DISTINCT child_id) as families FROM (
//larger query
)a
I'll get 3 which is not correct.
Alternatively, how can I incorporate a WHERE clause that checks DISTINCT guardian_id's? Any other approaches?
Also note that there are instances where a child may have one guardian only.
To get the distinct family you can try the following approach.
select distinct array_agg(distinct guardian_id)
from family
group by child_id;
The above query will return the list of unique families.
eg.
{1,2}
{3,4}
Now you can apply the count on top of it.

Joining cells together to shorten result in sql

Let's say my select statement in t-sql returns
John Smith | Chicago | a
John Smith | Chicago | f
John Smith | Chicago | j
Josh Dude | Houston | p
Josh Dude | Houston | s
And I want it to return
John Smith | Chicago | a, f, j
Josh Dude | Houston | p, s
How would I do that?
The first half of the article T-SQL: Normalized data to a single comma delineated string and back is a code and image-heavy tutorial on how you can use STUFF and a FOR XML PATH statement take a normalized set and turn one column into a single comma separated value.
Good luck.

MS Access Group By breaks when using a date

For some reason using a date/time field in a select query with Group By in Access 2010 breaks (records are not properly "grouped by" the text field first, showing the same "aTextField" value multiple times). I am able to replicate the issue in a simple, one table query. Ex:
SELECT aTextField, SUM(aIntField) AS SumOfaIntField
FROM simpleTable
GROUP BY aTextField, aDateField
HAVING aDateField >= Date()
ORDER BY aTextField;
As soon as you remove the "aDateField" from the query (Group By and Having lines) then it works properly. I can even remove the HAVING line and it still breaks. Leaving me to believe that it is something with the Group By.
Any feedback would be great. Thanks!
EDIT More details
**simpleTable**
--------------------------------------------
| ID | aTextField | aIntField | aDateField |
============================================
| 1 | John Doe | 1 | 3/14/2013 |
| 2 | John Doe | | 3/15/2013 |
| 3 | Jane Doe | 1 | 3/15/2013 |
| 4 | John Doe | 2 | 3/18/2013 |
| 5 | Jane Doe | 1 | 3/19/2013 |
| 6 | John Doe | | 3/20/2013 |
| 7 | John Doe | 3 | 3/21/2013 |
| 8 | Jane Doe | 1 | 3/19/2013 |
| 9 | John Doe | | 3/22/2013 |
| 10 | Jane Doe | 2 | 3/20/2013 |
| 11 | Jane Doe | | 3/21/2013 |
| 12 | Jane Doe | | 3/22/2013 |
--------------------------------------------
**Expected Result**
-------------------------------
| aTextField | SumOfaIntField |
===============================
| Jane Doe | 4 |
| John Doe | 3 |
-------------------------------
**Actual Result**
-------------------------------
| aTextField | SumOfaIntField |
===============================
| Jane Doe | 2 |
| Jane Doe | 2 |
| Jane Doe | |
| Jane Doe | |
| John Doe | |
| John Doe | 3 |
| John Doe | |
-------------------------------
So what appears to be happening is that there is a seperate row for each date as well. I just need to filter by the date and not necessarily Group By it. However, Access will not accept the query without grouping it. Options?
You're grouping by aTextField and aDateField. Perhaps simpleTable includes rows where the date is the same, but the time of day is different. In that case your grouping would produce a row for each date/time combination.
Whether or not that was the explanation, you should check what the db engine actually evaluates by including aDateField in the SELECT list.
SELECT aTextField, aDateField, SUM(aIntField)
FROM simpleTable
GROUP BY aTextField, aDateField
HAVING aDateField >= Date()
ORDER BY aTextField;
Also consider using a WHERE instead of HAVING clause:
WHERE aDateField >= Date()
Based on your sample data, I suspect you want ...
SELECT aTextField, SUM(aIntField)
FROM simpleTable
GROUP BY aTextField
WHERE aDateField >= Date()
ORDER BY aTextField;
You should be able to use the following:
SELECT aTextField, SUM(aIntField) AS SumOfaIntField
FROM simpleTable
WHERE aDateField >= Date()
GROUP BY aTextField
ORDER BY aTextField;
You will notice that I removed the GROUP BY on the aDateField column. Since you want the total for each aTextField, then you do not need to group by the date. Grouping by date will result in a separate row for each distinct date.
Note: this query was tested in MS Access 2010 and generated your desired result.
I think you are misunderstanding on how GROUP BY works. You should be seeing the same aTextField once for each unique textfield/datetime combination
Sample
a 2012-01-01
a 2012-01-01
b 2012-01-01
b 2012-01-02
b 2012-01-02
group by aTextField, aDateField
a 2012-01-01
b 2012-01-01
b 2012-01-02
group by aTextField
a
b