I would like to create a data return from a non existing table:
+-------+--------+------------+
| type | name | expiration |
+-------+--------+------------+
| fruit | orange | 1999-12-31 |
| fruit | banana | 1999-12-31 |
| fruit | apple | 1999-12-31 |
| fruit | orange | 2000-01-01 |
| fruit | banana | 2000-01-01 |
| fruit | apple | 2000-01-01 |
+-------+--------+------------+
Where for each fruit there is a single row with the same date. Then the date is incremented by one day and for that date there is created a row for each fruit.
So far I'm having this query:
WITH RECURSIVE cte
AS (
SELECT
"fruit" as `type`
,"orange" as `name`
,"1999-12-31" as `expiration`
UNION ALL
SELECT
"fruit" as `type`
,"banana" as `name`
,date_add(`expiration`, INTERVAL 1 DAY) as `expiration`
FROM cte
WHERE `expiration` < "2000-01-01"
)
SELECT *
FROM cte
;
that generates:
+-------+--------+------------+
| type | name | expiration |
+-------+--------+------------+
| fruit | orange | 1999-12-31 |
| fruit | banana | 2000-01-01 |
+-------+--------+------------+
I think there could be solution to that problem by doing inside of the recursive CTE select from temporary fruit_list table that has fruits' names but I don't know how to implement that.
The example fruit_list table:
CREATE TEMPORARY TABLE IF NOT EXISTS `fruit_list` (
`name` varchar(128) NOT NULL
) ENGINE = InnoDB;
INSERT INTO `fruit_list` VALUES
("orange")
,("banana")
,("apple")
;
I would like to solve the problem with a regular query instead of procedure. Is it possible?
The aim of the solution it to have a query that can return some test data for each fruit and date range.
You could handle this via a series of cross joins:
SELECT
f.type,
n.name,
e.expiration
FROM (SELECT 'fruit' AS type) f
CROSS JOIN
(
SELECT 'orange' AS name UNION ALL
SELECT 'banana' UNION ALL
SELECT 'apple'
) n
CROSS JOIN
(
SELECT '1999-12-31' AS expiration UNION ALL
SELECT '2000-01-01'
) e
ORDER BY
f.type,
e.expiration,
n.name;
Demo
For those who would like to use temporary table here's the code:
CREATE TEMPORARY TABLE IF NOT EXISTS `fruit_list` (
`name` varchar(128) NOT NULL
) ENGINE = InnoDB;
INSERT INTO `fruit_list` VALUES
("orange")
,("banana")
,("apple")
;
WITH RECURSIVE cte
AS (
SELECT
"1999-12-30" as `expiration`
UNION ALL
SELECT
date_add(`expiration`, INTERVAL 1 DAY) as `expiration`
FROM cte
WHERE `expiration` < "2000-01-02"
)
,cte1 as (
SELECT * FROM cte
CROSS JOIN `fruit_list`
)
SELECT
"fruit" as `type`
,`name`
,`expiration`
FROM cte1
ORDER BY
`expiration`
,`name`
;
Result:
+-------+--------+------------+
| type | name | expiration |
+-------+--------+------------+
| fruit | apple | 1999-12-30 |
| fruit | banana | 1999-12-30 |
| fruit | orange | 1999-12-30 |
| fruit | apple | 1999-12-31 |
| fruit | banana | 1999-12-31 |
| fruit | orange | 1999-12-31 |
| fruit | apple | 2000-01-01 |
| fruit | banana | 2000-01-01 |
| fruit | orange | 2000-01-01 |
| fruit | apple | 2000-01-02 |
| fruit | banana | 2000-01-02 |
| fruit | orange | 2000-01-02 |
+-------+--------+------------+
12 rows in set (0.00 sec)
Related
I am looking to return non-grouped row values from a query of a table sorted by the MAX value of a column, within a group.
DATA TABLE
| NAME | ASSET | ACTION | DATE |
|--|--|--|--|
| JOE | CAR | BOUGHT | 1/1/2020 |
| JANE | HORSE | BOUGHT | 1/1/2021 |
| JOE | HORSE | BOUGHT | 2/1/2021 |
| JANE | HORSE | SOLD | 3/1/2021 |
| JOE | CAR | SOLD | 1/1/2022 |
| JOE | CAR | BOUGHT | 2/1/2022 |
For the table above, I presented the following code.
=QUERY(A1:D5,"SELECT A,B,C,D, MAX(D) GROUP BY A,B",TRUE)
The following TARGET TABLE is output I'm looking for:
| NAME | ASSET | ACTION | DATE |
|--|--|--|--|
| JANE | HORSE | SOLD | 3/1/2021 |
| JOE | HORSE | BOUGHT | 2/1/2021 |
| JOE | CAR | BOUGHT | 2/1/2022 |
However, because 'C' is not included in the GROUP, the formula returns an error. "Unable to parse query string for Function QUERY parameter 2: ADD_COL_TO_GROUP_BY_OR_AGG: C"
If I were to omit COL C & D, "ACTION" & "DATE" from the SELECT: =QUERY(A1:D5,"SELECT A,B, MAX(D) GROUP BY A,B",TRUE) , I have the correct record rows, but am missing the STATUS.
MAX-DATE TABLE
| NAME | ASSET | max DATE |
|--|--|--|
| JANE | HORSE | 3/1/2021 |
| JOE | HORSE | 2/1/2021 |
| JOE | CAR | 2/1/2022 |
OR, when I add COL C as a "PIVIOT": =QUERY(A1:D5,"SELECT A,B, MAX(D) GROUP BY A,B PIVOT C",TRUE)I have the correct record rows, but do not have the 'current' STATUS within the record row.
PIVOT ACTION TABLE
| NAME | ASSET | BOUGHT | SOLD |
|--|--|--|--|
| JANE | HORSE | 1/1/2021 | 3/1/2021 |
| JOE | HORSE | 2/1/2021 | |
| JOE | CAR | 2/1/2022 | 1/1/2022 |
Still I have not found a method to create my TARGET TABLE.
Am I overlooking a method to include a non-grouped field into a query using MAX()? Or is it impossible within Google Sheets Query without JOIN functions?
(I hope it is obvious that I desire to apply this to a large and dynamic dataset.)
Thank you for your insight. Cheers!
It's not that flexible to work with QUERYs with its aggregation requisites and so on.
You can create a filter, by comparing column D with a "fictional" column created with BYROW: = BYROW(A2:A,LAMBDA(each,MAXIFS($D$2:$D,$A$2:$A,each,$B$2:$B,OFFSET(each,,1))))
That would look like this (I highlighted the matches and added extra rows for reference):
Then, you can set this filter (don't create this column, it's just a visualization of what I did):
=FILTER(A2:D,D2:D = BYROW(A2:A,LAMBDA(each,MAXIFS($D$2:$D,$A$2:$A,each,$B$2:$B,OFFSET(each,,1)))))
This way, you're comparing the dates with the maximum for each category
I use Google Spreadsheet to keep track of my wine cellar, with a simple sheet with number of bottles / name of the wine / where it's from :
+--------------+------------+-------------+
| # of bottles | Wine | Appellation |
+--------------+------------+-------------+
| 2 | Talbot | St Julien |
| 16 | Marbuzet | St Estephe |
| 1 | Terrebrune | Bandol |
| 10 | Madiniere | Cote Rotie |
+--------------+------------+-------------+
I'd like to get a roundup of appellation I have the most, sorted by number of bottles, eg:
+--------------+-------------+
| # of bottles | Appellation |
+--------------+-------------+
| 16 | St Estephe |
| 10 | Cote Rotie |
| ... | ... |
+--------------+-------------+
I know how to get the sorted list of appellations (=sort(UNIQUE($C$2:$C$999) with wine origin in column C) and the matching number of bottles (=SUMIFS(A:A,C:C,<cell with appellation name>), but I'm stuck at sorting by the number of bottles instead.
With QUERY
=QUERY(A:C,"select sum(A),C group by C order by sum(A) desc",1)
To rename the header:
=QUERY(A:C,"select sum(A),C group by C order by sum(A) desc label sum(A) '# of bottles'",1)
With SORT and SUMIF
=ArrayFormula(SORT({SUMIF(C:C,UNIQUE(C2:C),A:A),UNIQUE(C2:C)},1,FALSE))
What array formula would work for this?
Test Sheet: Open
Current Data Structure
Contains a running list of names and when they started, ended training.
| A | B | C |
| John | StartDate1 | EndDate1 |
| Adam | StartDate3 | EndDate3 |
| John | StartDate2 | EndDate2 |
| Ted | StartDate5 | EndDate5 |
| Adam | StartDate4 | EndDate4 |
Expected Results
Unique column of names in column E =UNIQUE(A2:A)
Next to the unique name, display every StartDate & EndDate that matches the unique name.
| E | F | G | H | I |
| John | StartDate1 | EndDate1 | StartDate2 | EndDate2 |
| Adam | StartDate3 | EndDate3 | StartDate4 | EndDate4 |
| Ted | StartDate4 | EndDate4 | | |
What I have tried
=FILTER(B2:C,A2:A = E2)
Does not return on a single row. ❌
Does not work with ARRAYFORMULA. ❌
=TRANSPOSE(FILTER(B2:C,A2:A = E2:E))
Returns all StartDates on a single row, and all End Dates on the next row. ❌
It should return on a single row (StartDate,EndDate,StartDate,EndDate, etc)
Does not work with ARRAYFORMULA. ❌
=ARRAYFORMULA(VLOOKUP(E2:E,A2:C,{2,3}))
Returns the first match only ❌
Works with array formula. ✔️
What am I doing wrong? Is there a better arrayformula that can display every start and end date that matches a unique name in a row?
Thanks for your help!
use:
=INDEX(SPLIT(FLATTEN(QUERY(QUERY(IF(A3:A="",,{A3:A, "×"&B3:B&"×"&C3:C}),
"select max(Col2) where Col2 is not null group by Col2 pivot Col1"),,9^9)), "×"))
I am trying to build an expense dashboard in google sheets for my personal use.
I have data that I will pull from my receipts like so:
First sheet: "Expenses Feb 18"
+------------+--------+--------+
| Item | Amount | Type |
+------------+--------+--------+
| Tomatoes | 2.39 | veggie |
| Joghurt | 1.45 | dairy |
| mozzarella | 1.99 | dairy |
| macadamia | 4.59 | nuts |
+------------+--------+--------+
Second table: "Categories"
+------------+----------+-----------+---------------+
| dairy | veggie | nuts | uncategorised |
+------------+----------+-----------+---------------+
| joghurt | tomatoes | macadamia | a |
| mozzarella | cucumber | pecan | b |
| feta | | | c |
| | | | d-z |
| | | | 0-9 |
| | | | - |
| | | | _ |
+------------+----------+-----------+---------------+
I want to automatically fill out the type column based on the item name.
So far I have a regex that is able to match an item. It will print the matched string. But what I need is the column name (header). And it has to be able to loop through the columns. This only works for a single column.
=REGEXEXTRACT(C11, JOIN("|", INDIRECT("Categories!A1:A"&COUNTA(Categories!A:A))))
The second table is not a desirable way to enter data. Data should be entered preferably with more rows than columns ( not in a pivoted manner).
=ARRAYFORMULA(CONCATENATE(IF(A16=$C$24:$E$25,C$23:E$23,)))
A16 : 🍅
C24:E25: Category table
C23:E23: Category header.
Currently, for a recurring search with different parameters, I have this ActiveRecord query built:
current_user.documents.order(:updated_at).reverse_order.includes(:groups,:rules)
Now, usually I tack on a where clause to this to perform this search. However, I now need to do a search through the jsonb field for all rows that have a certain value as in the key:value pair. I've been able to do something a similar to that in my SQL, with this syntax (the data field will only be exactly two levels nested):
SELECT
*
FROM
(SELECT
*
FROM
(SELECT
*
FROM
documents
) A,
jsonb_each(A.data)
) B,
jsonb_each_text(B.value) ASC C
WHERE
C.value = '30';
However, I want to use the current ActiveRecord search to make this query (which includes the groups/rules eager loading).
I'm struggling with the use of the comma, which I understand is an implicit join, which is executed before explicit joins, so when I try something like this:
select * from documents B join (select * from jsonb_each(B.data)) as A on true;
ERROR: invalid reference to FROM-clause entry for table "b"
LINE 1: ...* from documents B join (select * from jsonb_each(B.data)) a...
^
HINT: There is an entry for table "b", but it cannot be referenced from this part of the query.
But I don't understand how to reference the complete "table" the ActiveRecord query I have creates before I make a joins call, as well as make use of the comma syntax for implicit joins to work.
Also, I'm an SQL amateur, so if you see some improvements or other ways to do this, please do tell.
EDIT: Description of documents table:
Table "public.documents"
Column | Type | Modifiers | Storage | Stats target | Description
------------+-----------------------------+--------------------------------------------------------+----------+--------------+-------------
id | integer | not null default nextval('documents_id_seq'::regclass) | plain | |
document_id | character varying | | extended | |
name | character varying | | extended | |
size | integer | | plain | |
last_updated| timestamp without time zone | | plain | |
user_id | integer | | plain | |
created_at | timestamp without time zone | | plain | |
updated_at | timestamp without time zone | | plain | |
kind | character varying | | extended | |
uid | character varying | | extended | |
access_token_id | integer | | plain | |
data | jsonb | not null default '{}'::jsonb | extended | |
Indexes:
"documents_pkey" PRIMARY KEY, btree (id)
```
Sample rows, first would match a search for '30' (data is the last field):
2104 | 24419693037 | LsitHandsBackwards.jpg | | | 1 | 2017-06-25 21:45:49.121686 | 2017-07-01 21:32:37.624184 | box | 221607127 | 15 | {"owner": {"born": "to make history", "price": 30}}
2177 | /all-drive/uml flows/typicaluseractivity.svg | TypicalUserActivity.svg | 12375 | 2014-08-11 02:21:14 | 1 | 2017-07-07 14:00:11.487455 | 2017-07-07 14:00:11.487455 | dropbox | 325694961 | 20 | {"owner": {}}
You can use a query similar to the one you already showed:
SELECT
d.id, d.data
FROM
documents AS d
INNER JOIN json_each(d.data) AS x ON TRUE
INNER JOIN json_each(x.value) AS y ON TRUE
WHERE
cast(y.value as text) = '30';
Assuming your data would be the following one:
INSERT INTO documents
(data)
VALUES
('{"owner": {"born": "to make history", "price": 30}}'),
('{"owner": {}}'),
('{"owner": {"born": "to make history", "price": 50}, "seller": {"worth": 30}}')
;
The result you'd get is:
id | data
-: | :---------------------------------------------------------------------------
1 | {"owner": {"born": "to make history", "price": 30}}
3 | {"owner": {"born": "to make history", "price": 50}, "seller": {"worth": 30}}
You can check it (together with some step-by-step looks at the data) at dbfiddle here