@friedbotstudio/create-baseline 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/bin/cli.js +8 -2
- package/obj/template/.claude/skills/audit-baseline/audit.sh +11 -5
- package/obj/template/.claude/skills/google-analytics/SKILL.md +129 -0
- package/obj/template/.claude/skills/google-analytics/references/audiences.md +389 -0
- package/obj/template/.claude/skills/google-analytics/references/bigquery.md +470 -0
- package/obj/template/.claude/skills/google-analytics/references/custom-dimensions.md +355 -0
- package/obj/template/.claude/skills/google-analytics/references/custom-events.md +383 -0
- package/obj/template/.claude/skills/google-analytics/references/data-management.md +416 -0
- package/obj/template/.claude/skills/google-analytics/references/debugview.md +364 -0
- package/obj/template/.claude/skills/google-analytics/references/events-fundamentals.md +398 -0
- package/obj/template/.claude/skills/google-analytics/references/gtag.md +502 -0
- package/obj/template/.claude/skills/google-analytics/references/gtm-integration.md +483 -0
- package/obj/template/.claude/skills/google-analytics/references/measurement-protocol.md +519 -0
- package/obj/template/.claude/skills/google-analytics/references/privacy.md +441 -0
- package/obj/template/.claude/skills/google-analytics/references/recommended-events.md +464 -0
- package/obj/template/.claude/skills/google-analytics/references/reporting.md +397 -0
- package/obj/template/.claude/skills/google-analytics/references/setup.md +344 -0
- package/obj/template/.claude/skills/google-analytics/references/user-tracking.md +417 -0
- package/obj/template/.claude/skills/optimize-seo/SKILL.md +313 -0
- package/obj/template/.claude/skills/optimize-seo/scripts/pagespeed.mjs +197 -0
- package/obj/template/.claude/skills/pagespeed-insights/LICENSE.md +37 -0
- package/obj/template/.claude/skills/pagespeed-insights/SKILL.md +446 -0
- package/obj/template/.claude/skills/pagespeed-insights/reference.md +50 -0
- package/obj/template/CLAUDE.md +3 -3
- package/obj/template/docs/init/seed.md +2 -2
- package/obj/template/manifest.json +27 -6
- package/package.json +7 -2
- package/src/CLAUDE.template.md +3 -3
- package/src/cli/install.js +14 -4
- package/src/seed.template.md +2 -2
- package/obj/template/.claude/hooks/lib/__pycache__/resume_writer.cpython-314.pyc +0 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
# GA4 BigQuery Export and Analysis
|
|
2
|
+
|
|
3
|
+
Complete guide to GA4 BigQuery export including setup, schema, SQL query patterns, and data analysis.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
GA4 BigQuery export provides raw, event-level data access for advanced analysis, custom reporting, machine learning, and long-term data warehousing. Unlike GA4 reports, BigQuery data is unsampled and allows SQL-based analysis.
|
|
8
|
+
|
|
9
|
+
## Why Use BigQuery
|
|
10
|
+
|
|
11
|
+
| Benefit | Description |
|
|
12
|
+
|---------|-------------|
|
|
13
|
+
| Unsampled data | No sampling thresholds |
|
|
14
|
+
| Raw event data | Access every parameter |
|
|
15
|
+
| SQL analysis | Complex queries and joins |
|
|
16
|
+
| Data integration | Combine with other sources |
|
|
17
|
+
| Long-term storage | Beyond GA4 retention |
|
|
18
|
+
| Custom attribution | Build custom models |
|
|
19
|
+
| Machine learning | Train on GA4 data |
|
|
20
|
+
|
|
21
|
+
## BigQuery Export Setup
|
|
22
|
+
|
|
23
|
+
### Prerequisites
|
|
24
|
+
|
|
25
|
+
- GA4 property (standard or 360)
|
|
26
|
+
- Google Cloud project
|
|
27
|
+
- BigQuery API enabled
|
|
28
|
+
- Editor permissions on GA4 property
|
|
29
|
+
|
|
30
|
+
### Setup Steps
|
|
31
|
+
|
|
32
|
+
**Step 1: Create/Select Google Cloud Project**
|
|
33
|
+
|
|
34
|
+
1. Go to console.cloud.google.com
|
|
35
|
+
2. Create new project or select existing
|
|
36
|
+
3. Enable BigQuery API
|
|
37
|
+
|
|
38
|
+
**Step 2: Link GA4 to BigQuery**
|
|
39
|
+
|
|
40
|
+
1. GA4 Admin -> Product Links -> BigQuery Links
|
|
41
|
+
2. Click "Link"
|
|
42
|
+
3. Choose Google Cloud project
|
|
43
|
+
4. Select dataset location (US, EU, etc.)
|
|
44
|
+
5. Configure export:
|
|
45
|
+
- Daily: Complete export once per day
|
|
46
|
+
- Streaming: Real-time (360 only)
|
|
47
|
+
6. Include advertising IDs (optional)
|
|
48
|
+
7. Confirm setup
|
|
49
|
+
|
|
50
|
+
### Export Options
|
|
51
|
+
|
|
52
|
+
| Option | Description | Availability |
|
|
53
|
+
|--------|-------------|--------------|
|
|
54
|
+
| Daily Export | Once per day (~9 AM property timezone) | Standard GA4 |
|
|
55
|
+
| Streaming Export | Near real-time | GA4 360 only |
|
|
56
|
+
| Include Advertising IDs | For Ads integration | Optional |
|
|
57
|
+
|
|
58
|
+
### Data Availability
|
|
59
|
+
|
|
60
|
+
- Daily tables: ~24 hours after day ends
|
|
61
|
+
- Intraday tables: ~3 updates per day
|
|
62
|
+
- Streaming: Minutes after collection (360)
|
|
63
|
+
|
|
64
|
+
## Table Structure
|
|
65
|
+
|
|
66
|
+
### Table Naming
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
project.dataset.events_YYYYMMDD # Daily export
|
|
70
|
+
project.dataset.events_intraday_YYYYMMDD # Intraday
|
|
71
|
+
project.dataset.events_* # Wildcard all dates
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Key Schema Fields
|
|
75
|
+
|
|
76
|
+
#### Event Fields
|
|
77
|
+
|
|
78
|
+
| Field | Type | Description |
|
|
79
|
+
|-------|------|-------------|
|
|
80
|
+
| event_date | STRING | YYYYMMDD format |
|
|
81
|
+
| event_timestamp | INTEGER | Microseconds since epoch |
|
|
82
|
+
| event_name | STRING | Event name |
|
|
83
|
+
| event_params | RECORD (REPEATED) | Event parameters |
|
|
84
|
+
| event_value_in_usd | FLOAT | Event value |
|
|
85
|
+
|
|
86
|
+
#### User Fields
|
|
87
|
+
|
|
88
|
+
| Field | Type | Description |
|
|
89
|
+
|-------|------|-------------|
|
|
90
|
+
| user_id | STRING | User ID if set |
|
|
91
|
+
| user_pseudo_id | STRING | Anonymous ID |
|
|
92
|
+
| user_properties | RECORD (REPEATED) | User properties |
|
|
93
|
+
| user_first_touch_timestamp | INTEGER | First visit |
|
|
94
|
+
|
|
95
|
+
#### Device Fields
|
|
96
|
+
|
|
97
|
+
| Field | Type | Description |
|
|
98
|
+
|-------|------|-------------|
|
|
99
|
+
| device.category | STRING | desktop, mobile, tablet |
|
|
100
|
+
| device.operating_system | STRING | Windows, iOS, Android |
|
|
101
|
+
| device.browser | STRING | Chrome, Safari |
|
|
102
|
+
|
|
103
|
+
#### Geo Fields
|
|
104
|
+
|
|
105
|
+
| Field | Type | Description |
|
|
106
|
+
|-------|------|-------------|
|
|
107
|
+
| geo.country | STRING | Country name |
|
|
108
|
+
| geo.region | STRING | State/region |
|
|
109
|
+
| geo.city | STRING | City name |
|
|
110
|
+
|
|
111
|
+
#### Traffic Source Fields
|
|
112
|
+
|
|
113
|
+
| Field | Type | Description |
|
|
114
|
+
|-------|------|-------------|
|
|
115
|
+
| traffic_source.source | STRING | google, direct |
|
|
116
|
+
| traffic_source.medium | STRING | organic, cpc |
|
|
117
|
+
| traffic_source.name | STRING | Campaign name |
|
|
118
|
+
|
|
119
|
+
#### E-commerce Fields
|
|
120
|
+
|
|
121
|
+
| Field | Type | Description |
|
|
122
|
+
|-------|------|-------------|
|
|
123
|
+
| ecommerce.transaction_id | STRING | Transaction ID |
|
|
124
|
+
| ecommerce.purchase_revenue_in_usd | FLOAT | Purchase revenue |
|
|
125
|
+
| items | RECORD (REPEATED) | Items array |
|
|
126
|
+
|
|
127
|
+
## SQL Query Patterns
|
|
128
|
+
|
|
129
|
+
### Query 1: Event Count by Name
|
|
130
|
+
|
|
131
|
+
```sql
|
|
132
|
+
SELECT
|
|
133
|
+
event_name,
|
|
134
|
+
COUNT(*) as event_count
|
|
135
|
+
FROM
|
|
136
|
+
`project.dataset.events_*`
|
|
137
|
+
WHERE
|
|
138
|
+
_TABLE_SUFFIX BETWEEN '20250101' AND '20250131'
|
|
139
|
+
GROUP BY
|
|
140
|
+
event_name
|
|
141
|
+
ORDER BY
|
|
142
|
+
event_count DESC
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Query 2: Extract Event Parameters
|
|
146
|
+
|
|
147
|
+
```sql
|
|
148
|
+
SELECT
|
|
149
|
+
event_date,
|
|
150
|
+
event_name,
|
|
151
|
+
user_pseudo_id,
|
|
152
|
+
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') as page_location,
|
|
153
|
+
(SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_title') as page_title
|
|
154
|
+
FROM
|
|
155
|
+
`project.dataset.events_*`
|
|
156
|
+
WHERE
|
|
157
|
+
_TABLE_SUFFIX BETWEEN '20250101' AND '20250131'
|
|
158
|
+
AND event_name = 'page_view'
|
|
159
|
+
LIMIT 1000
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Query 3: Purchase Analysis
|
|
163
|
+
|
|
164
|
+
```sql
|
|
165
|
+
SELECT
|
|
166
|
+
event_date,
|
|
167
|
+
COUNT(DISTINCT user_pseudo_id) as purchasers,
|
|
168
|
+
COUNT(DISTINCT ecommerce.transaction_id) as transactions,
|
|
169
|
+
SUM(ecommerce.purchase_revenue_in_usd) as total_revenue,
|
|
170
|
+
AVG(ecommerce.purchase_revenue_in_usd) as avg_order_value
|
|
171
|
+
FROM
|
|
172
|
+
`project.dataset.events_*`
|
|
173
|
+
WHERE
|
|
174
|
+
_TABLE_SUFFIX BETWEEN '20250101' AND '20250131'
|
|
175
|
+
AND event_name = 'purchase'
|
|
176
|
+
AND ecommerce.transaction_id IS NOT NULL
|
|
177
|
+
GROUP BY
|
|
178
|
+
event_date
|
|
179
|
+
ORDER BY
|
|
180
|
+
event_date
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Query 4: UNNEST Items Array
|
|
184
|
+
|
|
185
|
+
```sql
|
|
186
|
+
SELECT
|
|
187
|
+
event_date,
|
|
188
|
+
item.item_name,
|
|
189
|
+
item.item_category,
|
|
190
|
+
SUM(item.quantity) as total_quantity,
|
|
191
|
+
SUM(item.item_revenue_in_usd) as total_revenue
|
|
192
|
+
FROM
|
|
193
|
+
`project.dataset.events_*`,
|
|
194
|
+
UNNEST(items) as item
|
|
195
|
+
WHERE
|
|
196
|
+
_TABLE_SUFFIX BETWEEN '20250101' AND '20250131'
|
|
197
|
+
AND event_name = 'purchase'
|
|
198
|
+
GROUP BY
|
|
199
|
+
event_date,
|
|
200
|
+
item.item_name,
|
|
201
|
+
item.item_category
|
|
202
|
+
ORDER BY
|
|
203
|
+
total_revenue DESC
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Query 5: User Journey Analysis
|
|
207
|
+
|
|
208
|
+
```sql
|
|
209
|
+
WITH user_events AS (
|
|
210
|
+
SELECT
|
|
211
|
+
user_pseudo_id,
|
|
212
|
+
event_timestamp,
|
|
213
|
+
event_name,
|
|
214
|
+
(SELECT value.string_value FROM UNNEST(event_params)
|
|
215
|
+
WHERE key = 'page_location') as page_location
|
|
216
|
+
FROM
|
|
217
|
+
`project.dataset.events_*`
|
|
218
|
+
WHERE
|
|
219
|
+
_TABLE_SUFFIX = '20250115'
|
|
220
|
+
)
|
|
221
|
+
SELECT
|
|
222
|
+
user_pseudo_id,
|
|
223
|
+
ARRAY_AGG(
|
|
224
|
+
STRUCT(event_name, page_location, event_timestamp)
|
|
225
|
+
ORDER BY event_timestamp
|
|
226
|
+
) as event_sequence
|
|
227
|
+
FROM
|
|
228
|
+
user_events
|
|
229
|
+
GROUP BY
|
|
230
|
+
user_pseudo_id
|
|
231
|
+
LIMIT 100
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Query 6: Session Attribution
|
|
235
|
+
|
|
236
|
+
```sql
|
|
237
|
+
SELECT
|
|
238
|
+
event_date,
|
|
239
|
+
traffic_source.source,
|
|
240
|
+
traffic_source.medium,
|
|
241
|
+
traffic_source.name as campaign,
|
|
242
|
+
COUNT(DISTINCT user_pseudo_id) as users,
|
|
243
|
+
COUNT(DISTINCT CONCAT(user_pseudo_id,
|
|
244
|
+
(SELECT value.int_value FROM UNNEST(event_params)
|
|
245
|
+
WHERE key = 'ga_session_id'))) as sessions
|
|
246
|
+
FROM
|
|
247
|
+
`project.dataset.events_*`
|
|
248
|
+
WHERE
|
|
249
|
+
_TABLE_SUFFIX BETWEEN '20250101' AND '20250131'
|
|
250
|
+
GROUP BY
|
|
251
|
+
event_date,
|
|
252
|
+
traffic_source.source,
|
|
253
|
+
traffic_source.medium,
|
|
254
|
+
traffic_source.name
|
|
255
|
+
ORDER BY
|
|
256
|
+
sessions DESC
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### Helper Functions
|
|
260
|
+
|
|
261
|
+
```sql
|
|
262
|
+
-- Reusable functions for parameter extraction
|
|
263
|
+
CREATE TEMP FUNCTION GetParamString(params ANY TYPE, target_key STRING)
|
|
264
|
+
RETURNS STRING
|
|
265
|
+
AS (
|
|
266
|
+
(SELECT value.string_value FROM UNNEST(params) WHERE key = target_key)
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
CREATE TEMP FUNCTION GetParamInt(params ANY TYPE, target_key STRING)
|
|
270
|
+
RETURNS INT64
|
|
271
|
+
AS (
|
|
272
|
+
(SELECT value.int_value FROM UNNEST(params) WHERE key = target_key)
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
-- Usage
|
|
276
|
+
SELECT
|
|
277
|
+
event_date,
|
|
278
|
+
GetParamString(event_params, 'page_location') as page_location,
|
|
279
|
+
GetParamInt(event_params, 'engagement_time_msec') as engagement_time
|
|
280
|
+
FROM
|
|
281
|
+
`project.dataset.events_*`
|
|
282
|
+
WHERE
|
|
283
|
+
_TABLE_SUFFIX BETWEEN '20250101' AND '20250131'
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Query Optimisation
|
|
287
|
+
|
|
288
|
+
### Best Practices
|
|
289
|
+
|
|
290
|
+
**1. Use _TABLE_SUFFIX Filtering:**
|
|
291
|
+
```sql
|
|
292
|
+
-- Good
|
|
293
|
+
WHERE _TABLE_SUFFIX BETWEEN '20250101' AND '20250131'
|
|
294
|
+
|
|
295
|
+
-- Bad (scans all partitions)
|
|
296
|
+
WHERE event_date BETWEEN '20250101' AND '20250131'
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
**2. Filter on Clustered Columns:**
|
|
300
|
+
```sql
|
|
301
|
+
-- Tables clustered by event_name and event_timestamp
|
|
302
|
+
WHERE event_name IN ('page_view', 'purchase')
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**3. Select Specific Columns:**
|
|
306
|
+
```sql
|
|
307
|
+
-- Good
|
|
308
|
+
SELECT event_name, user_pseudo_id, event_timestamp
|
|
309
|
+
|
|
310
|
+
-- Bad (high cost)
|
|
311
|
+
SELECT *
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
**4. Limit UNNEST Operations:**
|
|
315
|
+
```sql
|
|
316
|
+
-- Good: Inline UNNEST
|
|
317
|
+
(SELECT value.string_value FROM UNNEST(event_params)
|
|
318
|
+
WHERE key = 'page_location')
|
|
319
|
+
|
|
320
|
+
-- Avoid: Full UNNEST in FROM
|
|
321
|
+
FROM table, UNNEST(event_params) as param
|
|
322
|
+
WHERE param.key = 'page_location'
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**5. Use LIMIT During Development:**
|
|
326
|
+
```sql
|
|
327
|
+
LIMIT 1000 -- Test query first
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
## Cost Management
|
|
331
|
+
|
|
332
|
+
### BigQuery Pricing
|
|
333
|
+
|
|
334
|
+
| Type | Cost |
|
|
335
|
+
|------|------|
|
|
336
|
+
| Storage | ~$0.02/GB/month |
|
|
337
|
+
| Queries | ~$5/TB scanned |
|
|
338
|
+
| Streaming inserts | ~$0.05/GB (360 only) |
|
|
339
|
+
|
|
340
|
+
### Free Tier
|
|
341
|
+
|
|
342
|
+
- 10 GB storage free/month
|
|
343
|
+
- 1 TB queries free/month
|
|
344
|
+
|
|
345
|
+
### Reducing Costs
|
|
346
|
+
|
|
347
|
+
1. Partition by date using _TABLE_SUFFIX
|
|
348
|
+
2. Select only needed columns
|
|
349
|
+
3. Use LIMIT for testing
|
|
350
|
+
4. Create materialised views for frequent queries
|
|
351
|
+
5. Set up cost alerts in Google Cloud
|
|
352
|
+
|
|
353
|
+
## Data Retention
|
|
354
|
+
|
|
355
|
+
### GA4 vs BigQuery
|
|
356
|
+
|
|
357
|
+
| Platform | Retention |
|
|
358
|
+
|----------|-----------|
|
|
359
|
+
| GA4 Standard | 2 or 14 months |
|
|
360
|
+
| BigQuery | Unlimited (until deleted) |
|
|
361
|
+
|
|
362
|
+
### Setting Table Expiration
|
|
363
|
+
|
|
364
|
+
```sql
|
|
365
|
+
ALTER TABLE `project.dataset.events_20250101`
|
|
366
|
+
SET OPTIONS (
|
|
367
|
+
expiration_timestamp=TIMESTAMP "2026-01-01 00:00:00 UTC"
|
|
368
|
+
)
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
## Common Use Cases
|
|
372
|
+
|
|
373
|
+
### 1. Unsampled Reporting
|
|
374
|
+
|
|
375
|
+
GA4 UI may sample large datasets. BigQuery provides complete data.
|
|
376
|
+
|
|
377
|
+
### 2. Custom Attribution
|
|
378
|
+
|
|
379
|
+
- Access full user journey
|
|
380
|
+
- Build custom attribution models
|
|
381
|
+
- Credit touchpoints as needed
|
|
382
|
+
|
|
383
|
+
### 3. Data Integration
|
|
384
|
+
|
|
385
|
+
- Join GA4 with CRM data
|
|
386
|
+
- Combine with product catalogue
|
|
387
|
+
- Enrich with external sources
|
|
388
|
+
|
|
389
|
+
### 4. Machine Learning
|
|
390
|
+
|
|
391
|
+
- Export to ML tools
|
|
392
|
+
- Predict churn, LTV, conversions
|
|
393
|
+
- Train custom models
|
|
394
|
+
|
|
395
|
+
### 5. Long-term Analysis
|
|
396
|
+
|
|
397
|
+
- Historical analysis beyond GA4 limits
|
|
398
|
+
- Year-over-year comparisons
|
|
399
|
+
- Trend analysis
|
|
400
|
+
|
|
401
|
+
## Troubleshooting
|
|
402
|
+
|
|
403
|
+
### No Data in Tables
|
|
404
|
+
|
|
405
|
+
**Causes:**
|
|
406
|
+
- Link just created (wait 24 hours)
|
|
407
|
+
- Export paused
|
|
408
|
+
- Wrong project/dataset
|
|
409
|
+
|
|
410
|
+
**Solutions:**
|
|
411
|
+
1. Wait for first export
|
|
412
|
+
2. Check BigQuery Links status
|
|
413
|
+
3. Verify project configuration
|
|
414
|
+
|
|
415
|
+
### Missing Events
|
|
416
|
+
|
|
417
|
+
**Causes:**
|
|
418
|
+
- Events not firing
|
|
419
|
+
- Consent mode blocking
|
|
420
|
+
- Filter applied
|
|
421
|
+
|
|
422
|
+
**Solutions:**
|
|
423
|
+
1. Verify in DebugView first
|
|
424
|
+
2. Check consent configuration
|
|
425
|
+
3. Review data filters
|
|
426
|
+
|
|
427
|
+
### High Query Costs
|
|
428
|
+
|
|
429
|
+
**Causes:**
|
|
430
|
+
- SELECT * usage
|
|
431
|
+
- Missing date filters
|
|
432
|
+
- Large date ranges
|
|
433
|
+
|
|
434
|
+
**Solutions:**
|
|
435
|
+
1. Select specific columns
|
|
436
|
+
2. Always use _TABLE_SUFFIX
|
|
437
|
+
3. Narrow date ranges
|
|
438
|
+
|
|
439
|
+
## Quick Reference
|
|
440
|
+
|
|
441
|
+
### Table Names
|
|
442
|
+
|
|
443
|
+
- Daily: `events_YYYYMMDD`
|
|
444
|
+
- Intraday: `events_intraday_YYYYMMDD`
|
|
445
|
+
- Wildcard: `events_*`
|
|
446
|
+
|
|
447
|
+
### Date Filter
|
|
448
|
+
|
|
449
|
+
```sql
|
|
450
|
+
WHERE _TABLE_SUFFIX BETWEEN '20250101' AND '20250131'
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Extract Parameter
|
|
454
|
+
|
|
455
|
+
```sql
|
|
456
|
+
(SELECT value.string_value FROM UNNEST(event_params)
|
|
457
|
+
WHERE key = 'param_name')
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### UNNEST Items
|
|
461
|
+
|
|
462
|
+
```sql
|
|
463
|
+
FROM table, UNNEST(items) as item
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Costs
|
|
467
|
+
|
|
468
|
+
- Storage: $0.02/GB/month
|
|
469
|
+
- Queries: $5/TB scanned
|
|
470
|
+
- Free: 10 GB storage, 1 TB queries/month
|