@aryacode/dsh-engineering 0.5.0 → 0.6.0
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/CHANGELOG.md +20 -0
- package/README.md +6 -6
- package/cordis.patch.yml +1 -1
- package/entries/netlogic.js +266 -0
- package/entries/optix.js +13 -0
- package/package.json +3 -2
- package/vendor/optix/SKILL.md +68 -0
- package/vendor/optix/netlogic/SOURCE.json +12 -0
- package/vendor/optix/netlogic/index.json +1987 -0
- package/vendor/optix/netlogic/pages/accessing-project-nodes.md +113 -0
- package/vendor/optix/netlogic/pages/advanced-svg.md +124 -0
- package/vendor/optix/netlogic/pages/alarming.md +489 -0
- package/vendor/optix/netlogic/pages/async-tasks.md +236 -0
- package/vendor/optix/netlogic/pages/audit-signature.md +91 -0
- package/vendor/optix/netlogic/pages/colors.md +122 -0
- package/vendor/optix/netlogic/pages/companion-specs.md +77 -0
- package/vendor/optix/netlogic/pages/creating-objects.md +370 -0
- package/vendor/optix/netlogic/pages/database-interaction.md +437 -0
- package/vendor/optix/netlogic/pages/datagrids.md +123 -0
- package/vendor/optix/netlogic/pages/debugging-tips.md +605 -0
- package/vendor/optix/netlogic/pages/designtime-tags-import.md +531 -0
- package/vendor/optix/netlogic/pages/dialog-boxes.md +150 -0
- package/vendor/optix/netlogic/pages/dispatch-events.md +200 -0
- package/vendor/optix/netlogic/pages/dynamic-links.md +825 -0
- package/vendor/optix/netlogic/pages/element-access.md +1208 -0
- package/vendor/optix/netlogic/pages/engineering-units.md +298 -0
- package/vendor/optix/netlogic/pages/events.md +340 -0
- package/vendor/optix/netlogic/pages/execute-command.md +202 -0
- package/vendor/optix/netlogic/pages/fto-studio-cli.md +99 -0
- package/vendor/optix/netlogic/pages/good-practices.md +50 -0
- package/vendor/optix/netlogic/pages/information-model.md +89 -0
- package/vendor/optix/netlogic/pages/log-output.md +22 -0
- package/vendor/optix/netlogic/pages/managing-aliases.md +22 -0
- package/vendor/optix/netlogic/pages/manipulate-nodes.md +72 -0
- package/vendor/optix/netlogic/pages/mqtt.md +240 -0
- package/vendor/optix/netlogic/pages/netlogic-lifecycle.md +270 -0
- package/vendor/optix/netlogic/pages/netlogic-overview.md +155 -0
- package/vendor/optix/netlogic/pages/opcua.md +325 -0
- package/vendor/optix/netlogic/pages/plc-tags.md +219 -0
- package/vendor/optix/netlogic/pages/queries.md +1297 -0
- package/vendor/optix/netlogic/pages/random-generation.md +90 -0
- package/vendor/optix/netlogic/pages/recipes.md +375 -0
- package/vendor/optix/netlogic/pages/recipex.md +2383 -0
- package/vendor/optix/netlogic/pages/regex.md +15 -0
- package/vendor/optix/netlogic/pages/register-observers.md +346 -0
- package/vendor/optix/netlogic/pages/reports.md +141 -0
- package/vendor/optix/netlogic/pages/resource-uri.md +308 -0
- package/vendor/optix/netlogic/pages/runtime-tags-import.md +280 -0
- package/vendor/optix/netlogic/pages/serial-port.md +357 -0
- package/vendor/optix/netlogic/pages/sessions.md +58 -0
- package/vendor/optix/netlogic/pages/system-node.md +153 -0
- package/vendor/optix/netlogic/pages/template-library.md +127 -0
- package/vendor/optix/netlogic/pages/threading.md +236 -0
- package/vendor/optix/netlogic/pages/translations.md +417 -0
- package/vendor/optix/netlogic/pages/trends.md +128 -0
- package/vendor/optix/netlogic/pages/ui-animations.md +115 -0
- package/vendor/optix/netlogic/pages/users-groups.md +416 -0
- package/vendor/optix/netlogic/pages/variables-formatting.md +16 -0
- package/vendor/optix/netlogic/pages/variables-interaction.md +557 -0
- package/vendor/optix/netlogic/pages/variables-structure.md +155 -0
|
@@ -0,0 +1,1297 @@
|
|
|
1
|
+
# Database queries
|
|
2
|
+
|
|
3
|
+
> [!NOTE]
|
|
4
|
+
> As databases are only accessible at runtime, all the queries described in this page can only be used at runtime.
|
|
5
|
+
|
|
6
|
+
## Table of Contents
|
|
7
|
+
|
|
8
|
+
- [Acknowledgements](#acknowledgements)
|
|
9
|
+
- [Guidance and Best Practices](#guidance-and-best-practices)
|
|
10
|
+
- [SELECT queries](#select-queries)
|
|
11
|
+
- [UPDATE Queries](#update-queries)
|
|
12
|
+
- [DELETE Queries](#delete-queries)
|
|
13
|
+
- [INSERT Queries](#insert-queries)
|
|
14
|
+
- [Temporary Tables](#temporary-tables)
|
|
15
|
+
- [Dynamic queries using variables](#dynamic-queries-using-variables)
|
|
16
|
+
- [Known Limitations and Workarounds](#known-limitations-and-workarounds)
|
|
17
|
+
- [Queries on UI objects](#queries-on-ui-objects)
|
|
18
|
+
|
|
19
|
+
## Acknowledgements
|
|
20
|
+
|
|
21
|
+
FactoryTalk Optix does not support the full set of SQL queries, but has an own subset based on the SQL ANSI standard. The reason for this is that FactoryTalk Optix will automatically act as translation layer between the UI (where the custom queries are written) and the underlying database, which may be different based on the user selection (SQLite, SQL Server, MySQL, InfluxDB, etc).
|
|
22
|
+
|
|
23
|
+
This means that if the user later switches to a different database, the queries will still work without any change, as FactoryTalk Optix will take care of translating them to the right dialect. This is a great advantage for portability, but it also means that some queries that are valid in a specific SQL dialect may not be supported by FactoryTalk Optix if they are not compatible with all the supported databases.
|
|
24
|
+
|
|
25
|
+
## Guidance and Best Practices
|
|
26
|
+
|
|
27
|
+
### Best practices and gotchas
|
|
28
|
+
|
|
29
|
+
When writing queries for use in FactoryTalk Optix or similar embedded database contexts, keep these recommendations in mind:
|
|
30
|
+
|
|
31
|
+
- Prefer explicit column lists instead of `SELECT *` in production code to avoid unexpected schema changes and reduce bandwidth.
|
|
32
|
+
- When using `DISTINCT`, be explicit about the columns you need - `DISTINCT *` removes duplicate entire rows which can be expensive.
|
|
33
|
+
- Avoid updating temporary tables when portability is a concern some backends restrict updates on temporary objects.
|
|
34
|
+
- Use table aliases (for example `t1`, `t2`) when joining multiple tables to prevent ambiguity and improve readability.
|
|
35
|
+
- Be cautious with deep subqueries or multiple nested levels - they can impact performance and readability.
|
|
36
|
+
|
|
37
|
+
### SQL compatibility and versions
|
|
38
|
+
|
|
39
|
+
Some features documented here are available starting from specific versions of FactoryTalk Optix (noted inline). Always test queries on the target database before deploying to production.
|
|
40
|
+
|
|
41
|
+
### Security & parameterization
|
|
42
|
+
|
|
43
|
+
FactoryTalk Optix always provides parameterized queries when inserting data to the database using the dedicated `Insert` method of the store. This helps prevent SQL injection attacks and ensures data integrity.
|
|
44
|
+
|
|
45
|
+
## SELECT queries
|
|
46
|
+
|
|
47
|
+
### Select All Columns
|
|
48
|
+
|
|
49
|
+
Retrieves all columns from a table.
|
|
50
|
+
|
|
51
|
+
```sql
|
|
52
|
+
SELECT * FROM TestTable1
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### DISTINCT Qualifier
|
|
56
|
+
|
|
57
|
+
> [!NOTE]
|
|
58
|
+
> This feature is available starting from FactoryTalk Optix version 1.7.x.
|
|
59
|
+
|
|
60
|
+
The DISTINCT keyword eliminates duplicate rows from the result set. The ALL qualifier includes all rows, including duplicates (which is the default behavior).
|
|
61
|
+
|
|
62
|
+
```sql
|
|
63
|
+
SELECT DISTINCT Column1 FROM Table1
|
|
64
|
+
SELECT DISTINCT Column1, Column2 FROM Table1
|
|
65
|
+
SELECT DISTINCT * FROM Table1
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Select Specific Columns
|
|
69
|
+
|
|
70
|
+
Retrieves specific columns from a table.
|
|
71
|
+
|
|
72
|
+
```sql
|
|
73
|
+
SELECT ID, Name FROM TestTable1
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Column Aliases
|
|
77
|
+
|
|
78
|
+
Uses aliases to rename columns in the result set.
|
|
79
|
+
|
|
80
|
+
```sql
|
|
81
|
+
SELECT Name AS EmployeeName, Salary AS EmployeeSalary FROM TestTable1
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Filter Rows with Conditions
|
|
85
|
+
|
|
86
|
+
Filters rows based on conditions using WHERE clause.
|
|
87
|
+
|
|
88
|
+
```sql
|
|
89
|
+
SELECT * FROM TestTable1 WHERE Salary > 60000
|
|
90
|
+
SELECT * FROM TestTable1 WHERE Salary > 60000 AND DepartmentID = 101
|
|
91
|
+
SELECT * FROM TestTable1 WHERE DepartmentID = 101 OR DepartmentID = 102
|
|
92
|
+
SELECT * FROM TestTable1 WHERE NOT DepartmentID = 103
|
|
93
|
+
SELECT * FROM TestTable2 WHERE Location = 'Chicago'
|
|
94
|
+
SELECT Username FROM Users WHERE PrivateKey = 'test1234' AND Username <> 'test-user'
|
|
95
|
+
SELECT Username FROM Users WHERE PrivateKey = 'test1234' AND NOT (A = 6)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### IN
|
|
99
|
+
|
|
100
|
+
Filters rows where a column value matches any value in a specified list.
|
|
101
|
+
|
|
102
|
+
```sql
|
|
103
|
+
SELECT * FROM Table1 WHERE Column1 IN (10, 20, 30)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### BETWEEN
|
|
107
|
+
|
|
108
|
+
Filters rows where a column value falls within a specified range, inclusive of the boundaries.
|
|
109
|
+
|
|
110
|
+
```sql
|
|
111
|
+
SELECT * FROM Table1 WHERE Column1 BETWEEN 100 AND 200
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### LIKE
|
|
115
|
+
|
|
116
|
+
Filters rows using pattern matching with wildcards. Supports escape characters for literal matching.
|
|
117
|
+
|
|
118
|
+
```sql
|
|
119
|
+
SELECT * FROM Table1 WHERE column1 LIKE '%a'
|
|
120
|
+
SELECT * FROM Table1 WHERE column1 LIKE '%a%'
|
|
121
|
+
SELECT * FROM Table1 WHERE column1 LIKE '%bbpi!%ppo%' ESCAPE '!'
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Sorting Results
|
|
125
|
+
|
|
126
|
+
Orders the result set by specified columns.
|
|
127
|
+
|
|
128
|
+
```sql
|
|
129
|
+
SELECT * FROM TestTable1 ORDER BY Salary ASC
|
|
130
|
+
SELECT * FROM TestTable1 ORDER BY Salary DESC
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Fully Qualified Identifiers in ORDER BY
|
|
134
|
+
|
|
135
|
+
> [!NOTE]
|
|
136
|
+
> This feature is available starting from FactoryTalk Optix version 1.7.x.
|
|
137
|
+
|
|
138
|
+
Supports fully qualified table.column references in the ORDER BY clause.
|
|
139
|
+
|
|
140
|
+
```sql
|
|
141
|
+
SELECT * FROM MyTable ORDER BY MyTable.MyColumn
|
|
142
|
+
|
|
143
|
+
SELECT Recipes.Name, RecipeMetadata_MyRecipeSchema.MyMetadata1
|
|
144
|
+
FROM Recipes JOIN RecipeMetadata_MyRecipeSchema ON Recipes.Id = RecipeMetadata_MyRecipeSchema.RecipeId
|
|
145
|
+
WHERE RecipeMetadata_MyRecipeSchema.MyMetadata1 = 'BB'
|
|
146
|
+
ORDER BY Recipes.Name
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Pagination
|
|
150
|
+
|
|
151
|
+
Limits the number of rows returned and supports offset.
|
|
152
|
+
|
|
153
|
+
```sql
|
|
154
|
+
SELECT * FROM TestTable1 LIMIT 5
|
|
155
|
+
SELECT * FROM TestTable1 LIMIT 5 OFFSET 5
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Complex Filtering with Multiple Conditions
|
|
159
|
+
|
|
160
|
+
Combines multiple conditions with AND and OR operators.
|
|
161
|
+
|
|
162
|
+
```sql
|
|
163
|
+
SELECT * FROM TestTable1
|
|
164
|
+
WHERE (Salary > 60000 AND DepartmentID = 101) OR
|
|
165
|
+
(Salary < 50000 AND DepartmentID = 102)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Combining * with Standard Column
|
|
169
|
+
|
|
170
|
+
Adds additional columns to the result set alongside all columns.
|
|
171
|
+
|
|
172
|
+
```sql
|
|
173
|
+
SELECT *, Timestamp FROM TestTable4
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Asterisked Identifiers
|
|
177
|
+
|
|
178
|
+
> [!NOTE]
|
|
179
|
+
> This feature is available starting from FactoryTalk Optix version 1.7.x.
|
|
180
|
+
|
|
181
|
+
Supports table.* syntax to select all columns from a specific table in JOIN queries.
|
|
182
|
+
|
|
183
|
+
```sql
|
|
184
|
+
SELECT A.*, B.* FROM Table1 AS A JOIN Table2 AS B on A.Id = B.Table1Id WHERE ...
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Combining * with Literal Values
|
|
188
|
+
|
|
189
|
+
Adds literal values as new columns alongside all existing columns.
|
|
190
|
+
|
|
191
|
+
```sql
|
|
192
|
+
SELECT *, 'StaticValue' AS LiteralColumn FROM TestTable4
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Special Characters in Identifiers
|
|
196
|
+
|
|
197
|
+
Handles column names with special characters using double quotes.
|
|
198
|
+
|
|
199
|
+
```sql
|
|
200
|
+
SELECT "Water level", ID FROM TestTable1
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### CASE WHEN Expression
|
|
204
|
+
|
|
205
|
+
> [!NOTE]
|
|
206
|
+
> This feature is available starting from FactoryTalk Optix version 1.7.x.
|
|
207
|
+
|
|
208
|
+
The CASE WHEN expression allows conditional logic in SQL statements, supporting both SELECT and UPDATE operations.
|
|
209
|
+
|
|
210
|
+
```sql
|
|
211
|
+
SELECT
|
|
212
|
+
CASE
|
|
213
|
+
WHEN Id = 1 THEN 'A'
|
|
214
|
+
WHEN Id = 2 THEN 'B'
|
|
215
|
+
END
|
|
216
|
+
FROM Table1
|
|
217
|
+
WHERE Id IN (1, 2, 3)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
```sql
|
|
221
|
+
SELECT
|
|
222
|
+
CASE
|
|
223
|
+
WHEN Id = 1 THEN 'A'
|
|
224
|
+
WHEN Id = 2 THEN 'B'
|
|
225
|
+
ELSE 'X'
|
|
226
|
+
END
|
|
227
|
+
FROM Table1
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```sql
|
|
231
|
+
UPDATE Table1 SET Value =
|
|
232
|
+
CASE
|
|
233
|
+
WHEN Id = 1 THEN 'A'
|
|
234
|
+
WHEN Id = 2 THEN 'B'
|
|
235
|
+
ELSE 'X'
|
|
236
|
+
END
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
```sql
|
|
240
|
+
UPDATE Table1 SET Value =
|
|
241
|
+
CASE
|
|
242
|
+
WHEN Id = 1 THEN 'A'
|
|
243
|
+
WHEN Id = 2 THEN 'B'
|
|
244
|
+
END
|
|
245
|
+
WHERE Id IN (1, 2, 3)
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### IS NULL and IS NOT NULL
|
|
249
|
+
|
|
250
|
+
Filters rows based on NULL values.
|
|
251
|
+
|
|
252
|
+
```sql
|
|
253
|
+
SELECT * FROM TestTable1 WHERE DepartmentID IS NULL
|
|
254
|
+
SELECT * FROM TestTable1 WHERE DepartmentID IS NOT NULL
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### NOT
|
|
258
|
+
|
|
259
|
+
The NOT operator negates conditions and applies to all other operators, such as IN, BETWEEN, EXISTS, IS NULL, etc.
|
|
260
|
+
|
|
261
|
+
```sql
|
|
262
|
+
SELECT * FROM Table1 WHERE column1 IS NOT NULL
|
|
263
|
+
SELECT * FROM Table1 WHERE column1 NOT IN (10, 20)
|
|
264
|
+
SELECT * FROM Table1 WHERE column1 NOT BETWEEN 100 AND 200
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Basic Aggregates
|
|
268
|
+
|
|
269
|
+
Performs basic aggregation operations on columns.
|
|
270
|
+
|
|
271
|
+
```sql
|
|
272
|
+
SELECT COUNT(*) AS TotalCount FROM TestTable1
|
|
273
|
+
SELECT AVG(Salary) AS AverageSalary FROM TestTable1
|
|
274
|
+
SELECT SUM(Salary) AS TotalSalary FROM TestTable1
|
|
275
|
+
SELECT MAX(Salary) AS HighestSalary FROM TestTable1
|
|
276
|
+
SELECT MIN(Salary) AS LowestSalary FROM TestTable1
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Group Aggregates with Multiple Conditions
|
|
280
|
+
|
|
281
|
+
Groups rows by DepartmentID, counts employees and sums salaries for those with salary > 50000.
|
|
282
|
+
|
|
283
|
+
**Example Output:**
|
|
284
|
+
|
|
285
|
+
| DepartmentID | EmployeeCount | TotalSalary |
|
|
286
|
+
|--------------|---------------|-------------|
|
|
287
|
+
| 101 | 3 | 210000 |
|
|
288
|
+
| 102 | 2 | 105000 |
|
|
289
|
+
|
|
290
|
+
```sql
|
|
291
|
+
SELECT DepartmentID, COUNT(*) AS EmployeeCount, SUM(Salary) AS TotalSalary
|
|
292
|
+
FROM TestTable1
|
|
293
|
+
WHERE Salary > 50000
|
|
294
|
+
GROUP BY DepartmentID
|
|
295
|
+
SELECT DepartmentID, AVG(Salary) AS AvgSalary
|
|
296
|
+
FROM TestTable1
|
|
297
|
+
GROUP BY DepartmentID
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Filter Aggregates with HAVING
|
|
301
|
+
|
|
302
|
+
Groups by department and calculates average salary, then filters groups where average salary > 65000.
|
|
303
|
+
|
|
304
|
+
**Example Output:**
|
|
305
|
+
|
|
306
|
+
| DepartmentID | AvgSalary |
|
|
307
|
+
|--------------|-----------|
|
|
308
|
+
| 101 | 67000 |
|
|
309
|
+
| 103 | 79000 |
|
|
310
|
+
|
|
311
|
+
```sql
|
|
312
|
+
SELECT DepartmentID, AVG(Salary) AS AvgSalary
|
|
313
|
+
FROM TestTable1
|
|
314
|
+
GROUP BY DepartmentID
|
|
315
|
+
HAVING AVG(Salary) > 65000
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Combining * with Aggregate Functions
|
|
319
|
+
|
|
320
|
+
Adds a total row count column to all columns from TestTable4 using window function.
|
|
321
|
+
|
|
322
|
+
**Example Output:**
|
|
323
|
+
|
|
324
|
+
| ID | Timestamp | EventName | Duration | TotalCount |
|
|
325
|
+
|-----|---------------------|----------------|----------|------------|
|
|
326
|
+
| 1 | 1/1/2025 12:00 PM | Maintenance | 2.5 | 13 |
|
|
327
|
+
| 2 | 1/1/2025 12:15 PM | Adjustment | 1.5 | 13 |
|
|
328
|
+
|
|
329
|
+
```sql
|
|
330
|
+
SELECT *, COUNT(*) OVER () AS TotalCount FROM TestTable4
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### INNER JOIN
|
|
334
|
+
|
|
335
|
+
Joins TestTable1 and TestTable2 on DepartmentID, returning employees with their department names.
|
|
336
|
+
|
|
337
|
+
**Input Tables:**
|
|
338
|
+
|
|
339
|
+
TestTable1 (Employees):
|
|
340
|
+
|
|
341
|
+
| ID | Name | DepartmentID |
|
|
342
|
+
|----|-------|--------------|
|
|
343
|
+
| 1 | Alice | 101 |
|
|
344
|
+
| 2 | Bob | 102 |
|
|
345
|
+
|
|
346
|
+
TestTable2 (Departments):
|
|
347
|
+
|
|
348
|
+
| DepartmentID | DepartmentName |
|
|
349
|
+
|--------------|----------------|
|
|
350
|
+
| 101 | Engineering |
|
|
351
|
+
| 102 | Sales |
|
|
352
|
+
|
|
353
|
+
**Output:**
|
|
354
|
+
|
|
355
|
+
| Name | DepartmentName |
|
|
356
|
+
|-------|----------------|
|
|
357
|
+
| Alice | Engineering |
|
|
358
|
+
| Bob | Sales |
|
|
359
|
+
|
|
360
|
+
```sql
|
|
361
|
+
SELECT t1.Name, t2.DepartmentName
|
|
362
|
+
FROM TestTable1 AS t1
|
|
363
|
+
INNER JOIN TestTable2 AS t2
|
|
364
|
+
ON t1.DepartmentID = t2.DepartmentID
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### INNER JOIN Without Table Name Aliases
|
|
368
|
+
|
|
369
|
+
Joins tables without using aliases.
|
|
370
|
+
|
|
371
|
+
```sql
|
|
372
|
+
SELECT TestTable1.Name, TestTable2.DepartmentName
|
|
373
|
+
FROM TestTable1
|
|
374
|
+
INNER JOIN TestTable2
|
|
375
|
+
ON TestTable1.DepartmentID = TestTable2.DepartmentID
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Aggregates with INNER JOIN
|
|
379
|
+
|
|
380
|
+
Joins employees and departments, then groups by department name to count employees and average salary.
|
|
381
|
+
|
|
382
|
+
**Example Output:**
|
|
383
|
+
|
|
384
|
+
| DepartmentName | EmployeeCount | AvgSalary |
|
|
385
|
+
|----------------|---------------|-----------|
|
|
386
|
+
| Engineering | 3 | 67000 |
|
|
387
|
+
| HR | 2 | 79000 |
|
|
388
|
+
| IT | 2 | 64000 |
|
|
389
|
+
|
|
390
|
+
```sql
|
|
391
|
+
SELECT t2.DepartmentName, COUNT(t1.ID) AS EmployeeCount, AVG(t1.Salary) AS AvgSalary
|
|
392
|
+
FROM TestTable1 AS t1
|
|
393
|
+
INNER JOIN TestTable2 AS t2
|
|
394
|
+
ON t1.DepartmentID = t2.DepartmentID
|
|
395
|
+
GROUP BY t2.DepartmentName
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
### LEFT JOIN
|
|
399
|
+
|
|
400
|
+
Includes all rows from left table and matching from right.
|
|
401
|
+
|
|
402
|
+
```sql
|
|
403
|
+
SELECT t1.Name, t2.Location FROM TestTable1 AS t1 LEFT JOIN TestTable2 AS t2 ON t1.DepartmentID = t2.DepartmentID
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### LEFT JOIN with Filtering
|
|
407
|
+
|
|
408
|
+
Performs LEFT JOIN between employees and departments, then filters out rows where department name is NULL.
|
|
409
|
+
|
|
410
|
+
**Example Output:**
|
|
411
|
+
|
|
412
|
+
| Name | DepartmentName |
|
|
413
|
+
|---------|----------------|
|
|
414
|
+
| Alice | Engineering |
|
|
415
|
+
| Bob | Sales |
|
|
416
|
+
|
|
417
|
+
```sql
|
|
418
|
+
SELECT t1.Name, t2.DepartmentName
|
|
419
|
+
FROM TestTable1 AS t1
|
|
420
|
+
LEFT JOIN TestTable2 AS t2
|
|
421
|
+
ON t1.DepartmentID = t2.DepartmentID
|
|
422
|
+
WHERE t2.DepartmentName IS NOT NULL
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Aggregates with LEFT JOIN
|
|
426
|
+
|
|
427
|
+
LEFT JOIN employees and departments, group by department name, count employees and average salary. Includes departments with no employees (NULL counts).
|
|
428
|
+
|
|
429
|
+
**Explanation:** A LEFT JOIN returns all rows from the left table (TestTable1), along with matching rows from the right table (TestTable2). When no match is found, NULL values are included for columns from the right table.
|
|
430
|
+
|
|
431
|
+
```sql
|
|
432
|
+
SELECT t2.DepartmentName,
|
|
433
|
+
COUNT(t1.ID) AS EmployeeCount,
|
|
434
|
+
AVG(t1.Salary) AS AvgSalary
|
|
435
|
+
FROM TestTable1 AS t1
|
|
436
|
+
LEFT JOIN TestTable2 AS t2
|
|
437
|
+
ON t1.DepartmentID = t2.DepartmentID
|
|
438
|
+
GROUP BY t2.DepartmentName
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### CROSS JOIN
|
|
442
|
+
|
|
443
|
+
Creates Cartesian product of employees and departments, pairing each employee with every department.
|
|
444
|
+
|
|
445
|
+
**Example Output (Partial):**
|
|
446
|
+
|
|
447
|
+
| Name | DepartmentName |
|
|
448
|
+
|-------|----------------|
|
|
449
|
+
| Alice | Engineering |
|
|
450
|
+
| Alice | Sales |
|
|
451
|
+
| Bob | Engineering |
|
|
452
|
+
| Bob | Sales |
|
|
453
|
+
|
|
454
|
+
**Explanation:** A CROSS JOIN produces a Cartesian product, where each row from the first table is paired with every row from the second table. This can result in a large dataset.
|
|
455
|
+
|
|
456
|
+
```sql
|
|
457
|
+
SELECT t1.Name, t2.DepartmentName
|
|
458
|
+
FROM TestTable1 AS t1
|
|
459
|
+
CROSS JOIN TestTable2 AS t2
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Subquery in FROM Clause
|
|
463
|
+
|
|
464
|
+
Uses a subquery to calculate average salaries per department, then filters departments with avg salary > 65000.
|
|
465
|
+
|
|
466
|
+
**Example Output:**
|
|
467
|
+
|
|
468
|
+
| DepartmentID | AvgSalary |
|
|
469
|
+
|--------------|-----------|
|
|
470
|
+
| 101 | 67000 |
|
|
471
|
+
| 103 | 79000 |
|
|
472
|
+
|
|
473
|
+
**Explanation:** The subquery calculates the average salary grouped by department, and the outer query filters the results to include only departments where the average salary exceeds 65,000.
|
|
474
|
+
|
|
475
|
+
```sql
|
|
476
|
+
SELECT *
|
|
477
|
+
FROM (
|
|
478
|
+
SELECT DepartmentID, AVG(Salary) AS AvgSalary
|
|
479
|
+
FROM TestTable1
|
|
480
|
+
GROUP BY DepartmentID
|
|
481
|
+
) AS SubQuery
|
|
482
|
+
WHERE AvgSalary > 65000
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Subquery with JOIN
|
|
486
|
+
|
|
487
|
+
Joins employees with a subquery that calculates average salary per department.
|
|
488
|
+
|
|
489
|
+
**Example Output:**
|
|
490
|
+
|
|
491
|
+
| Name | AvgSalary |
|
|
492
|
+
|-------|-----------|
|
|
493
|
+
| Alice | 67000 |
|
|
494
|
+
| Bob | 55500 |
|
|
495
|
+
|
|
496
|
+
**Explanation:** The subquery calculates the average salary for each department. The main query then joins this result with TestTable1 to associate employee names with their respective department's average salary.
|
|
497
|
+
|
|
498
|
+
```sql
|
|
499
|
+
SELECT t1.Name, SubQuery.AvgSalary
|
|
500
|
+
FROM TestTable1 AS t1
|
|
501
|
+
INNER JOIN (
|
|
502
|
+
SELECT DepartmentID, AVG(Salary) AS AvgSalary
|
|
503
|
+
FROM TestTable1
|
|
504
|
+
GROUP BY DepartmentID
|
|
505
|
+
) AS SubQuery
|
|
506
|
+
ON t1.DepartmentID = SubQuery.DepartmentID
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### Subquery in WHERE Clause for IN Condition
|
|
510
|
+
|
|
511
|
+
Filters employees whose salary is in the list of salaries from department 101.
|
|
512
|
+
|
|
513
|
+
**Example Output:**
|
|
514
|
+
|
|
515
|
+
| Name |
|
|
516
|
+
|----------|
|
|
517
|
+
| Alice |
|
|
518
|
+
| Charlie |
|
|
519
|
+
| Grace |
|
|
520
|
+
|
|
521
|
+
**Description:** Subqueries in the WHERE clause with the IN operator are supported. This query correctly filtered names where Salary matches the subquery result.
|
|
522
|
+
|
|
523
|
+
```sql
|
|
524
|
+
SELECT Name FROM TestTable1 WHERE Salary IN (SELECT Salary FROM TestTable1 WHERE DepartmentID = 101)
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
### Subquery in WHERE Clause with Comparison
|
|
528
|
+
|
|
529
|
+
> [!NOTE]
|
|
530
|
+
> This feature is available starting from FactoryTalk Optix version 1.7.x.
|
|
531
|
+
|
|
532
|
+
Supports subqueries in WHERE clause using comparison operators.
|
|
533
|
+
|
|
534
|
+
```sql
|
|
535
|
+
SELECT * FROM Table1 WHERE Column > (SELECT Value FROM Table2)
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### EXISTS Operator
|
|
539
|
+
|
|
540
|
+
#### Simple EXISTS Subquery
|
|
541
|
+
|
|
542
|
+
Finds employees in departments located in 'New York' using EXISTS subquery.
|
|
543
|
+
|
|
544
|
+
**Example Output:**
|
|
545
|
+
|
|
546
|
+
| Name |
|
|
547
|
+
|----------|
|
|
548
|
+
| Alice |
|
|
549
|
+
| Charlie |
|
|
550
|
+
| Grace |
|
|
551
|
+
|
|
552
|
+
**Description:** Queries with EXISTS are supported, correctly identifying employees in departments located in "New York."
|
|
553
|
+
|
|
554
|
+
```sql
|
|
555
|
+
SELECT Name FROM TestTable1 WHERE EXISTS (SELECT 1 FROM TestTable2 WHERE TestTable2.DepartmentID = TestTable1.DepartmentID AND TestTable2.Location = 'New York')
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
#### Only extracting the recipes with the highest version for each recipe name:
|
|
559
|
+
|
|
560
|
+
When using the new [RecipeX](./recipex.md) module, multiple recipes with same name and different versions can be created. This query will extract only the recipes with the highest version for each recipe name, by using a subquery in the WHERE clause to filter out recipes that have a higher version available.
|
|
561
|
+
|
|
562
|
+
```sql
|
|
563
|
+
SELECT
|
|
564
|
+
R.*,
|
|
565
|
+
M.*
|
|
566
|
+
FROM Recipes AS R
|
|
567
|
+
JOIN RecipeMetadata_RecipeSchema1 AS M
|
|
568
|
+
ON R.Id = M.RecipeId
|
|
569
|
+
WHERE R.RecipeSchemaName = {#RecipeSchemaName:sql_literal}
|
|
570
|
+
AND NOT EXISTS (
|
|
571
|
+
SELECT 1
|
|
572
|
+
FROM Recipes AS R2
|
|
573
|
+
WHERE R2.RecipeSchemaName = R.RecipeSchemaName
|
|
574
|
+
AND R2.Name = R.Name
|
|
575
|
+
AND R2.Version > R.Version
|
|
576
|
+
)
|
|
577
|
+
ORDER BY R.Name
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**NOTE:** This query should be placed in a `string formatter` and the parameter `RecipeSchemaName` should be passed with a DynamicLink to retrieve the list of recipes for a given recipe schema name.
|
|
581
|
+
|
|
582
|
+
### Subquery in FROM Clause (Aggregation)
|
|
583
|
+
|
|
584
|
+
Uses subquery for aggregation in FROM.
|
|
585
|
+
|
|
586
|
+
```sql
|
|
587
|
+
SELECT AVG(Salary) AS AvgSalary FROM (SELECT * FROM TestTable1 WHERE DepartmentID = 101) AS SubQuery
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
### Multiple Subqueries with IN
|
|
591
|
+
|
|
592
|
+
Combines multiple IN subqueries.
|
|
593
|
+
|
|
594
|
+
```sql
|
|
595
|
+
SELECT Name FROM TestTable1 WHERE DepartmentID IN (SELECT DepartmentID FROM TestTable2 WHERE Location = 'New York') AND Salary IN (SELECT Salary FROM TestTable1 WHERE DepartmentID = 101)
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
### Multiple Subqueries with EXISTS
|
|
599
|
+
|
|
600
|
+
Combines multiple EXISTS subqueries.
|
|
601
|
+
|
|
602
|
+
```sql
|
|
603
|
+
SELECT Name FROM TestTable1 WHERE EXISTS (SELECT 1 FROM TestTable2 WHERE TestTable1.DepartmentID = TestTable2.DepartmentID AND TestTable2.Location = 'Chicago') AND EXISTS (SELECT 1 FROM TestTable1 AS Sub WHERE Sub.Salary > 60000 AND Sub.DepartmentID = 101)
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
### Mixed IN and EXISTS with Correlation
|
|
607
|
+
|
|
608
|
+
Mixes IN and EXISTS with correlation.
|
|
609
|
+
|
|
610
|
+
```sql
|
|
611
|
+
SELECT Name FROM TestTable1 WHERE DepartmentID IN (SELECT DepartmentID FROM TestTable2 WHERE Location = 'Chicago') AND EXISTS (SELECT 1 FROM TestTable2 WHERE TestTable2.DepartmentID = TestTable1.DepartmentID AND TestTable2.Location = 'New York')
|
|
612
|
+
```
|
|
613
|
+
|
|
614
|
+
### Subquery with Nested IN Clauses
|
|
615
|
+
|
|
616
|
+
Nested IN conditions in subquery.
|
|
617
|
+
|
|
618
|
+
```sql
|
|
619
|
+
SELECT Name FROM TestTable1 WHERE DepartmentID IN (SELECT DepartmentID FROM TestTable2 WHERE Location IN (SELECT Location FROM TestTable2 WHERE DepartmentID = 101))
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### Subquery with Nested EXISTS Clauses
|
|
623
|
+
|
|
624
|
+
Nested EXISTS conditions.
|
|
625
|
+
|
|
626
|
+
```sql
|
|
627
|
+
SELECT Name FROM TestTable1 WHERE EXISTS (SELECT 1 FROM TestTable2 WHERE TestTable1.DepartmentID = TestTable2.DepartmentID AND EXISTS (SELECT 1 FROM TestTable1 AS Sub WHERE Sub.Salary > 70000))
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Deeper Nested Subquery with IN
|
|
631
|
+
|
|
632
|
+
Deeper nesting with IN.
|
|
633
|
+
|
|
634
|
+
```sql
|
|
635
|
+
SELECT Name FROM TestTable1 WHERE DepartmentID IN (SELECT DepartmentID FROM TestTable2 WHERE Location IN (SELECT Location FROM TestTable2 WHERE Location IN (SELECT Location FROM TestTable2 WHERE DepartmentID = 101)))
|
|
636
|
+
```
|
|
637
|
+
|
|
638
|
+
### Deeper Nested Subquery with EXISTS
|
|
639
|
+
|
|
640
|
+
Deeper nesting with EXISTS.
|
|
641
|
+
|
|
642
|
+
```sql
|
|
643
|
+
SELECT Name FROM TestTable1 WHERE EXISTS (SELECT 1 FROM TestTable2 WHERE TestTable1.DepartmentID = TestTable2.DepartmentID AND EXISTS (SELECT 1 FROM TestTable1 WHERE EXISTS (SELECT 1 FROM TestTable2 WHERE TestTable2.DepartmentID = TestTable1.DepartmentID)))
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
### Subquery with Nested IN Clauses and Conditions
|
|
647
|
+
|
|
648
|
+
Complex nested conditions.
|
|
649
|
+
|
|
650
|
+
```sql
|
|
651
|
+
SELECT Name FROM TestTable1 WHERE DepartmentID IN (SELECT DepartmentID FROM TestTable2 WHERE Location IN (SELECT Location FROM TestTable2 WHERE EXISTS (SELECT 1 FROM TestTable1 WHERE Salary > 70000)))
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
### Subquery in JOIN Condition with Explicit Alias
|
|
655
|
+
|
|
656
|
+
Subquery in JOIN ON clause.
|
|
657
|
+
|
|
658
|
+
```sql
|
|
659
|
+
SELECT t1.Name, t2.DepartmentName FROM TestTable1 AS t1 INNER JOIN (SELECT * FROM TestTable2 WHERE Location = 'Chicago') AS t2 ON t1.DepartmentID = t2.DepartmentID
|
|
660
|
+
```
|
|
661
|
+
|
|
662
|
+
### Validate Joins with Nested Subqueries and Alias
|
|
663
|
+
|
|
664
|
+
Nested subqueries in JOIN.
|
|
665
|
+
|
|
666
|
+
```sql
|
|
667
|
+
SELECT t1.Name FROM TestTable1 AS t1 INNER JOIN (SELECT DepartmentID FROM TestTable2 WHERE EXISTS (SELECT 1 FROM TestTable1 WHERE TestTable1.DepartmentID = TestTable2.DepartmentID)) AS t2 ON t1.DepartmentID = t2.DepartmentID
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
### Combined Subqueries and Joins with Explicit Alias
|
|
671
|
+
|
|
672
|
+
Combines subqueries and joins.
|
|
673
|
+
|
|
674
|
+
```sql
|
|
675
|
+
SELECT t1.Name, t2.DepartmentName FROM TestTable1 AS t1 LEFT JOIN (SELECT DepartmentID, DepartmentName FROM TestTable2 WHERE EXISTS (SELECT 1 FROM TestTable1 WHERE Salary > 60000)) AS t2 ON t1.DepartmentID = t2.DepartmentID
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### Subqueries in FROM Clause (Filtered)
|
|
679
|
+
|
|
680
|
+
Filtered subquery in FROM.
|
|
681
|
+
|
|
682
|
+
```sql
|
|
683
|
+
SELECT * FROM (SELECT * FROM TestTable4 WHERE Duration > 2) AS SubQuery
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### ROW_NUMBER Window Function
|
|
687
|
+
|
|
688
|
+
Assigns unique sequential numbers to rows within each department partition, ordered by salary descending.
|
|
689
|
+
|
|
690
|
+
**Example Output:**
|
|
691
|
+
|
|
692
|
+
| Name | DepartmentID | Rank |
|
|
693
|
+
|---------|--------------|------|
|
|
694
|
+
| Grace | 101 | 1 |
|
|
695
|
+
| Charlie | 101 | 2 |
|
|
696
|
+
| Alice | 101 | 3 |
|
|
697
|
+
|
|
698
|
+
**Explanation:** The ROW_NUMBER function assigns a unique rank to each row within a partition (grouped by DepartmentID), ordered by Salary in descending order.
|
|
699
|
+
|
|
700
|
+
```sql
|
|
701
|
+
SELECT Name, DepartmentID,
|
|
702
|
+
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS Rank
|
|
703
|
+
FROM TestTable1
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### RANK Window Function
|
|
707
|
+
|
|
708
|
+
Assigns ranks within department partitions, handling ties by giving same rank to equal salaries.
|
|
709
|
+
|
|
710
|
+
**Explanation:** The RANK function assigns a rank to each row within a partition, ordered by Salary in descending order. Tied rows receive the same rank, with subsequent ranks skipping as needed.
|
|
711
|
+
|
|
712
|
+
```sql
|
|
713
|
+
SELECT Name, DepartmentID,
|
|
714
|
+
RANK() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC) AS Rank
|
|
715
|
+
FROM TestTable1
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
### SUM with PARTITION BY
|
|
719
|
+
|
|
720
|
+
Calculates total salary for each department and adds it to every row in that department.
|
|
721
|
+
|
|
722
|
+
**Example Output:**
|
|
723
|
+
|
|
724
|
+
| Name | DepartmentID | TotalDeptSalary |
|
|
725
|
+
|---------|--------------|-----------------|
|
|
726
|
+
| Alice | 101 | 201000 |
|
|
727
|
+
| Charlie | 101 | 201000 |
|
|
728
|
+
|
|
729
|
+
**Explanation:** The SUM function calculates the total Salary for all employees within each DepartmentID. This value is applied to every row in the respective partition.
|
|
730
|
+
|
|
731
|
+
```sql
|
|
732
|
+
SELECT Name, DepartmentID,
|
|
733
|
+
SUM(Salary) OVER (PARTITION BY DepartmentID) AS TotalDeptSalary
|
|
734
|
+
FROM TestTable1
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
### AVG with PARTITION BY
|
|
738
|
+
|
|
739
|
+
Calculates averages within partitions.
|
|
740
|
+
|
|
741
|
+
```sql
|
|
742
|
+
SELECT Name, DepartmentID,
|
|
743
|
+
AVG(Salary) OVER (PARTITION BY DepartmentID) AS AvgDeptSalary
|
|
744
|
+
FROM TestTable1
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
### Global Ranking Using ROW_NUMBER
|
|
748
|
+
|
|
749
|
+
Ranks all employees globally by salary in descending order.
|
|
750
|
+
|
|
751
|
+
**Example Output:**
|
|
752
|
+
|
|
753
|
+
| Name | Salary | DepartmentID | GlobalRank |
|
|
754
|
+
|-------|--------|--------------|------------|
|
|
755
|
+
| Diana | 80000 | 103 | 1 |
|
|
756
|
+
| Ivy | 78000 | 103 | 2 |
|
|
757
|
+
| Grace | 71000 | 101 | 3 |
|
|
758
|
+
|
|
759
|
+
**Explanation:** The ROW_NUMBER function assigns a unique global rank to each employee, ordered by salary in descending order. No partitioning is applied, so the ranking is across all rows.
|
|
760
|
+
|
|
761
|
+
```sql
|
|
762
|
+
SELECT Name, Salary, DepartmentID,
|
|
763
|
+
ROW_NUMBER() OVER (ORDER BY Salary DESC) AS GlobalRank
|
|
764
|
+
FROM TestTable1
|
|
765
|
+
```
|
|
766
|
+
|
|
767
|
+
### Ranking Within Department: Lowest to Highest Salary
|
|
768
|
+
|
|
769
|
+
Ranks within department by ascending salary.
|
|
770
|
+
|
|
771
|
+
```sql
|
|
772
|
+
SELECT Name, DepartmentID,
|
|
773
|
+
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary ASC) AS RankLowToHigh
|
|
774
|
+
FROM TestTable1
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
### ROW_NUMBER with Multiple Columns in ORDER BY
|
|
778
|
+
|
|
779
|
+
Ranks with tie-breaking by multiple columns.
|
|
780
|
+
|
|
781
|
+
```sql
|
|
782
|
+
SELECT Name, DepartmentID,
|
|
783
|
+
ROW_NUMBER() OVER (PARTITION BY DepartmentID ORDER BY Salary DESC, Name ASC) AS Rank
|
|
784
|
+
FROM TestTable1
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
### Combining * with a Window Function
|
|
788
|
+
|
|
789
|
+
Adds window function column to all columns.
|
|
790
|
+
|
|
791
|
+
```sql
|
|
792
|
+
SELECT *, ROW_NUMBER() OVER (ORDER BY Timestamp) AS RowNum FROM TestTable4
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
### Extract Year, Month, Day, Hour, Minute, and Second
|
|
796
|
+
|
|
797
|
+
Extracts all datetime components from Timestamp column.
|
|
798
|
+
|
|
799
|
+
**Example Output (Partial):**
|
|
800
|
+
|
|
801
|
+
| ID | Year | Month | Day | Hour | Minute | Second |
|
|
802
|
+
|-----|------|-------|-----|------|--------|--------|
|
|
803
|
+
| 1 | 2025 | 1 | 1 | 12 | 0 | 0 |
|
|
804
|
+
| 12 | 2025 | 2 | 28 | 23 | 59 | 59 |
|
|
805
|
+
| 13 | 2024 | 2 | 29 | 12 | 0 | 0 |
|
|
806
|
+
|
|
807
|
+
**Explanation:** The query uses the EXTRACT operator multiple times within the same SELECT statement to retrieve and display year, month, day, hour, minute, and second for each record in TestTable4.
|
|
808
|
+
|
|
809
|
+
```sql
|
|
810
|
+
SELECT ID,
|
|
811
|
+
EXTRACT(YEAR FROM Timestamp) AS Year,
|
|
812
|
+
EXTRACT(MONTH FROM Timestamp) AS Month,
|
|
813
|
+
EXTRACT(DAY FROM Timestamp) AS Day,
|
|
814
|
+
EXTRACT(HOUR FROM Timestamp) AS Hour,
|
|
815
|
+
EXTRACT(MINUTE FROM Timestamp) AS Minute,
|
|
816
|
+
EXTRACT(SECOND FROM Timestamp) AS Second
|
|
817
|
+
FROM TestTable4
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### Average Duration for Each Hour
|
|
821
|
+
|
|
822
|
+
Groups events by hour and calculates average duration for each hour.
|
|
823
|
+
|
|
824
|
+
**Example Output:**
|
|
825
|
+
|
|
826
|
+
| Hour | AvgDuration |
|
|
827
|
+
|------|-------------|
|
|
828
|
+
| 00 | 2.8 |
|
|
829
|
+
| 08 | 3.75 |
|
|
830
|
+
| 12 | 2.5625 |
|
|
831
|
+
| 23 | 2.93 |
|
|
832
|
+
|
|
833
|
+
**Explanation:** The AVG function calculates the average Duration for each hour extracted from the Timestamp. Grouping by Hour ensures the calculation is performed for each unique hour.
|
|
834
|
+
|
|
835
|
+
```sql
|
|
836
|
+
SELECT EXTRACT(HOUR FROM Timestamp) AS Hour,
|
|
837
|
+
AVG(Duration) AS AvgDuration
|
|
838
|
+
FROM TestTable4
|
|
839
|
+
GROUP BY Hour
|
|
840
|
+
ORDER BY Hour
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### Maximum Duration for Each Day
|
|
844
|
+
|
|
845
|
+
Groups events by day and finds maximum duration for each day.
|
|
846
|
+
|
|
847
|
+
**Example Output:**
|
|
848
|
+
|
|
849
|
+
| Day | MaxDuration |
|
|
850
|
+
|-----|-------------|
|
|
851
|
+
| 01 | 2.5 |
|
|
852
|
+
| 02 | 4.0 |
|
|
853
|
+
| 31 | 3.0 |
|
|
854
|
+
|
|
855
|
+
**Explanation:** The MAX function identifies the maximum Duration for each day extracted from the Timestamp. Grouping by Day ensures the calculation is performed for each unique day.
|
|
856
|
+
|
|
857
|
+
```sql
|
|
858
|
+
SELECT EXTRACT(DAY FROM Timestamp) AS Day,
|
|
859
|
+
MAX(Duration) AS MaxDuration
|
|
860
|
+
FROM TestTable4
|
|
861
|
+
GROUP BY Day
|
|
862
|
+
ORDER BY Day
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
### Event Count by Month
|
|
866
|
+
|
|
867
|
+
Groups events by month and counts total events per month.
|
|
868
|
+
|
|
869
|
+
**Example Output:**
|
|
870
|
+
|
|
871
|
+
| Month | EventCount |
|
|
872
|
+
|-------|------------|
|
|
873
|
+
| 01 | 11 |
|
|
874
|
+
| 02 | 2 |
|
|
875
|
+
|
|
876
|
+
**Explanation:** The COUNT(*) function calculates the total number of events for each month extracted from the Timestamp. Grouping by Month ensures events are counted for each unique month.
|
|
877
|
+
|
|
878
|
+
```sql
|
|
879
|
+
SELECT EXTRACT(MONTH FROM Timestamp) AS Month,
|
|
880
|
+
COUNT(*) AS EventCount
|
|
881
|
+
FROM TestTable4
|
|
882
|
+
GROUP BY Month
|
|
883
|
+
ORDER BY Month
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
### Grouping and Summing by Hourly Periods
|
|
887
|
+
|
|
888
|
+
Groups by year, month, day, hour and sums duration.
|
|
889
|
+
|
|
890
|
+
```sql
|
|
891
|
+
SELECT
|
|
892
|
+
EXTRACT(YEAR FROM Timestamp) AS Year,
|
|
893
|
+
EXTRACT(MONTH FROM Timestamp) AS Month,
|
|
894
|
+
EXTRACT(DAY FROM Timestamp) AS Day,
|
|
895
|
+
EXTRACT(HOUR FROM Timestamp) AS Hour,
|
|
896
|
+
SUM(Duration) AS TotalDuration
|
|
897
|
+
FROM TestTable4
|
|
898
|
+
GROUP BY Year, Month, Day, Hour
|
|
899
|
+
ORDER BY Year, Month, Day, Hour
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
### Grouping by Hour and Summing Duration
|
|
903
|
+
|
|
904
|
+
Groups by hour and sums duration.
|
|
905
|
+
|
|
906
|
+
```sql
|
|
907
|
+
SELECT EXTRACT(HOUR FROM Timestamp) AS Hour,
|
|
908
|
+
SUM(Duration) AS TotalDuration
|
|
909
|
+
FROM TestTable4
|
|
910
|
+
GROUP BY Hour
|
|
911
|
+
```
|
|
912
|
+
|
|
913
|
+
### Grouping by Full Hour Periods
|
|
914
|
+
|
|
915
|
+
Groups by full hour periods using subquery.
|
|
916
|
+
|
|
917
|
+
```sql
|
|
918
|
+
SELECT Year, Month, Day, Hour,
|
|
919
|
+
SUM(Duration) AS TotalDuration
|
|
920
|
+
FROM (
|
|
921
|
+
SELECT
|
|
922
|
+
EXTRACT(YEAR FROM Timestamp) AS Year,
|
|
923
|
+
EXTRACT(MONTH FROM Timestamp) AS Month,
|
|
924
|
+
EXTRACT(DAY FROM Timestamp) AS Day,
|
|
925
|
+
EXTRACT(HOUR FROM Timestamp) AS Hour,
|
|
926
|
+
Duration
|
|
927
|
+
FROM TestTable4
|
|
928
|
+
) AS SubQuery
|
|
929
|
+
GROUP BY Year, Month, Day, Hour
|
|
930
|
+
ORDER BY Year, Month, Day, Hour
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
### Verifying Grouping and Ordering by Extracted Datetime Data in a Subquery with Non-Selected Columns
|
|
934
|
+
|
|
935
|
+
Groups by extracted data with additional columns.
|
|
936
|
+
|
|
937
|
+
```sql
|
|
938
|
+
SELECT EventName,
|
|
939
|
+
SUM(Duration) AS TotalDuration
|
|
940
|
+
FROM (
|
|
941
|
+
SELECT
|
|
942
|
+
EXTRACT(YEAR FROM Timestamp) AS Year,
|
|
943
|
+
EXTRACT(MONTH FROM Timestamp) AS Month,
|
|
944
|
+
EXTRACT(DAY FROM Timestamp) AS Day,
|
|
945
|
+
EXTRACT(HOUR FROM Timestamp) AS Hour,
|
|
946
|
+
Duration,
|
|
947
|
+
EventName
|
|
948
|
+
FROM TestTable4
|
|
949
|
+
) AS SubQuery
|
|
950
|
+
GROUP BY Year, Month, Day, Hour
|
|
951
|
+
ORDER BY Year, Month, Day, Hour
|
|
952
|
+
```
|
|
953
|
+
|
|
954
|
+
### Grouping with Subquery and Extracted Values
|
|
955
|
+
|
|
956
|
+
Groups using subquery with extracted values.
|
|
957
|
+
|
|
958
|
+
```sql
|
|
959
|
+
SELECT Hour,
|
|
960
|
+
SUM(Duration) AS TotalDuration
|
|
961
|
+
FROM (
|
|
962
|
+
SELECT EXTRACT(HOUR FROM Timestamp) AS Hour,
|
|
963
|
+
Duration
|
|
964
|
+
FROM TestTable4
|
|
965
|
+
) AS SubQuery
|
|
966
|
+
GROUP BY Hour
|
|
967
|
+
ORDER BY Hour
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
### Filtering by EXTRACT in WHERE Clause (Year)
|
|
971
|
+
|
|
972
|
+
Filters by year using string comparison.
|
|
973
|
+
|
|
974
|
+
```sql
|
|
975
|
+
SELECT EXTRACT(HOUR FROM Timestamp) AS Hour, SUM(Duration) AS TotalDuration
|
|
976
|
+
FROM TestTable4
|
|
977
|
+
WHERE EXTRACT(YEAR FROM Timestamp) = '2025'
|
|
978
|
+
GROUP BY Hour
|
|
979
|
+
ORDER BY Hour
|
|
980
|
+
```
|
|
981
|
+
|
|
982
|
+
### Filtering by EXTRACT in WHERE Clause (Month)
|
|
983
|
+
|
|
984
|
+
Filters by month using string comparison.
|
|
985
|
+
|
|
986
|
+
```sql
|
|
987
|
+
SELECT EXTRACT(DAY FROM Timestamp) AS Day, AVG(Duration) AS AvgDuration
|
|
988
|
+
FROM TestTable4
|
|
989
|
+
WHERE EXTRACT(MONTH FROM Timestamp) = '01'
|
|
990
|
+
GROUP BY Day
|
|
991
|
+
ORDER BY Day
|
|
992
|
+
```
|
|
993
|
+
|
|
994
|
+
## UPDATE Queries
|
|
995
|
+
|
|
996
|
+
### Basic UPDATE
|
|
997
|
+
|
|
998
|
+
Simple single-row update (set Salary for a single Id):
|
|
999
|
+
|
|
1000
|
+
```sql
|
|
1001
|
+
UPDATE UpdateTest SET Salary = 56000 WHERE Id = 2
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
Update rows by matching text column (rename a person):
|
|
1005
|
+
|
|
1006
|
+
```sql
|
|
1007
|
+
UPDATE UpdateTest SET Name = 'Eve-Updated' WHERE Name = 'Eve'
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
Update multiple rows using IN:
|
|
1011
|
+
|
|
1012
|
+
```sql
|
|
1013
|
+
UPDATE UpdateTest SET Salary = Salary + 1000 WHERE Id IN (1, 3)
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
### Grouped UPDATE queries
|
|
1017
|
+
|
|
1018
|
+
These are used as a CASE/branching workaround when CASE expressions inside UPDATE may not be accepted by the store (FactoryTalk Optix versions prior to 1.7.x):
|
|
1019
|
+
|
|
1020
|
+
```sql
|
|
1021
|
+
-- set High for very large salaries
|
|
1022
|
+
UPDATE UpdateTest SET Status = 'High' WHERE Salary >= 80000
|
|
1023
|
+
|
|
1024
|
+
-- set Medium for mid-range salaries
|
|
1025
|
+
UPDATE UpdateTest SET Status = 'Medium' WHERE Salary >= 65000 AND Salary < 80000
|
|
1026
|
+
|
|
1027
|
+
-- set Low for lower salaries
|
|
1028
|
+
UPDATE UpdateTest SET Status = 'Low' WHERE Salary < 65000
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
### Update with CASE WHEN:
|
|
1032
|
+
|
|
1033
|
+
> [!NOTE]
|
|
1034
|
+
> This feature is available starting from FactoryTalk Optix version 1.7.x.
|
|
1035
|
+
|
|
1036
|
+
```sql
|
|
1037
|
+
UPDATE UpdateTest
|
|
1038
|
+
SET Status = CASE
|
|
1039
|
+
WHEN Salary >= 80000 THEN 'High'
|
|
1040
|
+
WHEN Salary >= 65000 THEN 'Medium'
|
|
1041
|
+
ELSE 'Low'
|
|
1042
|
+
END
|
|
1043
|
+
```
|
|
1044
|
+
|
|
1045
|
+
If that raises a syntax error on your target (FactoryTalk Optix versions prior to 1.7.x), use the grouped-update workaround demonstrated earlier.
|
|
1046
|
+
|
|
1047
|
+
### UPDATE with schema-qualified table names
|
|
1048
|
+
|
|
1049
|
+
> [!NOTE]
|
|
1050
|
+
> This feature is available starting from FactoryTalk Optix version 1.7.x.
|
|
1051
|
+
|
|
1052
|
+
Updates values in a table, supporting schema-qualified table names.
|
|
1053
|
+
|
|
1054
|
+
```sql
|
|
1055
|
+
UPDATE schema1.table1 SET column1 = 3, ...
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
### Notes on unsupported or dialect-specific UPDATE forms
|
|
1059
|
+
|
|
1060
|
+
The following UPDATE forms were tested against the embedded database. Results are summarized and example workarounds are provided.
|
|
1061
|
+
|
|
1062
|
+
#### Subquery in SET (UPDATE ... SET col = (SELECT ...))
|
|
1063
|
+
|
|
1064
|
+
Attempting to set a column using a scalar subquery (for example, mapping values from another table in a single UPDATE statement) is currently not supported.
|
|
1065
|
+
|
|
1066
|
+
Example of unsupported query (will fail):
|
|
1067
|
+
|
|
1068
|
+
```sql
|
|
1069
|
+
UPDATE UpdateTest
|
|
1070
|
+
SET Status = (SELECT NewStatus FROM StatusMap WHERE StatusMap.OldStatus = UpdateTest.Status)
|
|
1071
|
+
WHERE Status IN (SELECT OldStatus FROM StatusMap)
|
|
1072
|
+
```
|
|
1073
|
+
|
|
1074
|
+
Workaround: perform one simple UPDATE per mapping row (this is compatible and fast for small mapping tables):
|
|
1075
|
+
|
|
1076
|
+
```sql
|
|
1077
|
+
-- mapping table has rows (OldStatus, NewStatus) = ('Active','Enabled'), ('Inactive','Disabled')
|
|
1078
|
+
UPDATE UpdateTest SET Status = 'Enabled' WHERE Status = 'Active'
|
|
1079
|
+
UPDATE UpdateTest SET Status = 'Disabled' WHERE Status = 'Inactive'
|
|
1080
|
+
```
|
|
1081
|
+
|
|
1082
|
+
#### UPDATE ... FROM / join-style UPDATE
|
|
1083
|
+
|
|
1084
|
+
Some SQL dialects support `UPDATE ... FROM <join>` to update rows using a join with another table. These queries are not supported and will produce a syntax error.
|
|
1085
|
+
|
|
1086
|
+
Example of unsupported query (will fail):
|
|
1087
|
+
|
|
1088
|
+
```sql
|
|
1089
|
+
UPDATE UpdateTest SET Status = StatusMap.NewStatus FROM StatusMap WHERE UpdateTest.Status = StatusMap.OldStatus
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
Workaround: use per-mapping UPDATE statements as shown above, or run a small client-side loop that queries mapping rows and issues single UPDATE statements for each mapping.
|
|
1093
|
+
|
|
1094
|
+
#### Correlated EXISTS in UPDATE WHERE
|
|
1095
|
+
|
|
1096
|
+
Running `UPDATE ... WHERE EXISTS (SELECT 1 FROM Other WHERE Other.key = Target.key ...)` is currently not supported. If you rely on correlated `EXISTS` for updates, translate to an explicit set of `WHERE` conditions or use per-row updates retrieved by a separate `SELECT`.
|
|
1097
|
+
|
|
1098
|
+
FactoryTalk Optix reliably accepts straightforward `UPDATE ... SET ... WHERE <condition>` forms. More advanced forms that embed subqueries inside the `SET` expression, use correlated `EXISTS` in `WHERE`, or use `UPDATE ... FROM` may fail depending on FactoryTalk Optix version. When unsupported, the recommended approach is to break the update into multiple simple `UPDATE` statements or perform the logic in the client and apply targeted `UPDATE`s.
|
|
1099
|
+
|
|
1100
|
+
## DELETE Queries
|
|
1101
|
+
|
|
1102
|
+
### Basic DELETE
|
|
1103
|
+
|
|
1104
|
+
Deletes rows from a table based on a condition.
|
|
1105
|
+
|
|
1106
|
+
```sql
|
|
1107
|
+
DELETE FROM DeleteTestDemo WHERE id = 1
|
|
1108
|
+
```
|
|
1109
|
+
|
|
1110
|
+
### Delete multiple rows using IN
|
|
1111
|
+
|
|
1112
|
+
```sql
|
|
1113
|
+
DELETE FROM DeleteTestDemo WHERE id IN (2,3,4)
|
|
1114
|
+
```
|
|
1115
|
+
|
|
1116
|
+
### Delete using a subquery
|
|
1117
|
+
|
|
1118
|
+
```sql
|
|
1119
|
+
DELETE FROM DeleteTestDemo WHERE id IN (SELECT id FROM OtherTable WHERE Flag = 1)
|
|
1120
|
+
```
|
|
1121
|
+
|
|
1122
|
+
### Delete all rows (use with caution)
|
|
1123
|
+
|
|
1124
|
+
```sql
|
|
1125
|
+
DELETE FROM DeleteTestDemo
|
|
1126
|
+
```
|
|
1127
|
+
|
|
1128
|
+
### DELETE using a subquery (non-correlated example)
|
|
1129
|
+
|
|
1130
|
+
```sql
|
|
1131
|
+
DELETE FROM DeleteTestDemo WHERE GroupID IN (SELECT GroupID FROM OtherTable WHERE Flag = 1)
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
### Pattern and range deletes
|
|
1135
|
+
|
|
1136
|
+
```sql
|
|
1137
|
+
DELETE FROM DeleteTestExtra2 WHERE Val LIKE 'a%'
|
|
1138
|
+
DELETE FROM DeleteTestExtra2 WHERE Salary BETWEEN 50000 AND 53000
|
|
1139
|
+
```
|
|
1140
|
+
|
|
1141
|
+
### Notes on unsupported or dialect-specific DELETE forms
|
|
1142
|
+
|
|
1143
|
+
During testing the server did not accept several DELETE variants:
|
|
1144
|
+
|
|
1145
|
+
- Correlated EXISTS deletes in the form `DELETE ... WHERE EXISTS (SELECT ... WHERE o.col = target.col ...)` are not supported.
|
|
1146
|
+
- Multi-table DELETE / DELETE with JOIN (MySQL/SQL Server style) are not supported.
|
|
1147
|
+
- CTE-based DELETE (`WITH ... DELETE ...`) are not supported.
|
|
1148
|
+
- `DELETE ... RETURNING` and explicit transaction control (BEGIN/ROLLBACK) are not supported.
|
|
1149
|
+
|
|
1150
|
+
If you depend on any of the unsupported forms, ask for help translating to an equivalent supported pattern (for example, using `IN` with a subquery instead of a correlated `EXISTS`).
|
|
1151
|
+
|
|
1152
|
+
## INSERT Queries
|
|
1153
|
+
|
|
1154
|
+
`INSERT` queries are only allowed using the `Insert` method of the store. See the [Database interactions](./database-interaction.md) for details.
|
|
1155
|
+
|
|
1156
|
+
## Temporary Tables
|
|
1157
|
+
|
|
1158
|
+
Temporary tables provide a way to store intermediate results during query process. Key characteristics include:
|
|
1159
|
+
|
|
1160
|
+
- Temporary tables, identified by the `##` prefix, can be created and accessed using double-quoted identifiers.
|
|
1161
|
+
- Supported operations include querying, joining, and aggregations however, update operations are not permitted.
|
|
1162
|
+
- Temporary tables can be dropped successfully, facilitating proper resource management.
|
|
1163
|
+
|
|
1164
|
+
### Temporary Tables with Quoted Names
|
|
1165
|
+
|
|
1166
|
+
Creates temporary table with quoted name.
|
|
1167
|
+
|
|
1168
|
+
```sql
|
|
1169
|
+
CREATE TEMPORARY TABLE "##TempTable" AS SELECT DepartmentID, AVG(Salary) AS AvgSalary FROM TestTable1 GROUP BY DepartmentID
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
### Accessing Temporary Tables
|
|
1173
|
+
|
|
1174
|
+
Queries data from the temporary table created with department average salaries.
|
|
1175
|
+
|
|
1176
|
+
**Example Output:**
|
|
1177
|
+
|
|
1178
|
+
| DepartmentID | AvgSalary |
|
|
1179
|
+
|--------------|-----------|
|
|
1180
|
+
| 101 | 67000 |
|
|
1181
|
+
| 102 | 55500 |
|
|
1182
|
+
| 103 | 79000 |
|
|
1183
|
+
| 104 | 64000 |
|
|
1184
|
+
| 105 | 62000 |
|
|
1185
|
+
|
|
1186
|
+
```sql
|
|
1187
|
+
SELECT * FROM "##TempTable"
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
### Joining Temporary Tables
|
|
1191
|
+
|
|
1192
|
+
Joins employees table with temporary table containing department average salaries.
|
|
1193
|
+
|
|
1194
|
+
**Example Output:**
|
|
1195
|
+
|
|
1196
|
+
| Name | AvgSalary |
|
|
1197
|
+
|---------|-----------|
|
|
1198
|
+
| Alice | 67000 |
|
|
1199
|
+
| Bob | 55500 |
|
|
1200
|
+
| Charlie | 67000 |
|
|
1201
|
+
| Diana | 79000 |
|
|
1202
|
+
| Eve | 64000 |
|
|
1203
|
+
|
|
1204
|
+
```sql
|
|
1205
|
+
SELECT t1.Name, t2.AvgSalary FROM TestTable1 AS t1 INNER JOIN "##TempTable" AS t2 ON t1.DepartmentID = t2.DepartmentID
|
|
1206
|
+
```
|
|
1207
|
+
|
|
1208
|
+
### Dropping Temporary Tables
|
|
1209
|
+
|
|
1210
|
+
Removes temporary table.
|
|
1211
|
+
|
|
1212
|
+
```sql
|
|
1213
|
+
DROP TABLE "##TempTable"
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
### Filtered Temporary Tables
|
|
1217
|
+
|
|
1218
|
+
Creates temporary table with filtered data.
|
|
1219
|
+
|
|
1220
|
+
```sql
|
|
1221
|
+
CREATE TEMPORARY TABLE "##FilteredTemp" AS SELECT ID, Name, Salary FROM TestTable1 WHERE Salary > 60000
|
|
1222
|
+
```
|
|
1223
|
+
|
|
1224
|
+
### Accessing Filtered Temporary Tables
|
|
1225
|
+
|
|
1226
|
+
Queries the temporary table filtered to employees with salary > 60000.
|
|
1227
|
+
|
|
1228
|
+
**Example Output:**
|
|
1229
|
+
|
|
1230
|
+
| ID | Name | Salary |
|
|
1231
|
+
|----|---------|--------|
|
|
1232
|
+
| 3 | Charlie | 70000 |
|
|
1233
|
+
| 4 | Diana | 80000 |
|
|
1234
|
+
| 5 | Eve | 65000 |
|
|
1235
|
+
| 7 | Grace | 71000 |
|
|
1236
|
+
|
|
1237
|
+
```sql
|
|
1238
|
+
SELECT * FROM "##FilteredTemp"
|
|
1239
|
+
```
|
|
1240
|
+
|
|
1241
|
+
### Aggregations on Temporary Tables
|
|
1242
|
+
|
|
1243
|
+
Counts high-salary employees from the filtered temporary table.
|
|
1244
|
+
|
|
1245
|
+
**Example Output:**
|
|
1246
|
+
|
|
1247
|
+
| HighSalaryCount |
|
|
1248
|
+
|-----------------|
|
|
1249
|
+
| 7 |
|
|
1250
|
+
|
|
1251
|
+
```sql
|
|
1252
|
+
SELECT COUNT(*) AS HighSalaryCount FROM "##FilteredTemp"
|
|
1253
|
+
```
|
|
1254
|
+
|
|
1255
|
+
## Dynamic queries using variables
|
|
1256
|
+
|
|
1257
|
+
FactoryTalk Optix supports user-defined variables in SQL queries by using string formatting in your application code to construct the SQL query with the desired values before executing it against the database.
|
|
1258
|
+
|
|
1259
|
+
Variables has to be escaped or formatted using two dedicated formatters:
|
|
1260
|
+
|
|
1261
|
+
- `sql_literal` for string values, which adds single quotes, escapes internal quotes and formats dates appropriately.
|
|
1262
|
+
- `sql_identifier` for identifiers (like table or column names), which adds double quotes and escapes internal quotes.
|
|
1263
|
+
|
|
1264
|
+
For example:
|
|
1265
|
+
|
|
1266
|
+
```sql
|
|
1267
|
+
SELECT * FROM {#TableName:sql_identifier} WHERE Name = {#NameValue:sql_literal}
|
|
1268
|
+
```
|
|
1269
|
+
|
|
1270
|
+
## Known Limitations and Workarounds
|
|
1271
|
+
|
|
1272
|
+
As FactoryTalk Optix does not support all SQL features, some queries may fail due to syntax errors or unsupported constructs. If you encounter issues, consider the following workarounds:
|
|
1273
|
+
|
|
1274
|
+
- Use a StringFormatter to build dynamic queries that adjust based on supported features.
|
|
1275
|
+
- `CONCAT` and the `||` operator are not supported, but you can concatenate a string with something like `SELECT {#Param1:sql_literal} AS NewColumnName FROM Table` and compose the query dynamically.
|
|
1276
|
+
|
|
1277
|
+
## Queries on UI objects
|
|
1278
|
+
|
|
1279
|
+
### Populate a PieChart with the count of unique values in a column
|
|
1280
|
+
|
|
1281
|
+
This query returns the count of unique values in the column `Code` of the table `SQLiteStoreTable1` to populate a pie chard. Each slice of the pie chart will represent a unique value in the column `Code` and the size of the slice will be the count of the occurrences of that value.
|
|
1282
|
+
|
|
1283
|
+
The PieChard object should be populated with:
|
|
1284
|
+
|
|
1285
|
+
- Model: `EmbeddedDatabase1`
|
|
1286
|
+
- Query: `SELECT Code, COUNT(*) AS Count FROM SQLiteStoreTable1 GROUP BY Code ORDER BY Count DESC`
|
|
1287
|
+
- Label: `{Item}/Code`
|
|
1288
|
+
- Value: `{Item}/Count`
|
|
1289
|
+
|
|
1290
|
+
### Populate a Histogram chart with the count of unique values in a column
|
|
1291
|
+
|
|
1292
|
+
This query returns the count of unique values in the column `StatusMachine` of the table `SQLiteStoreTable1` to populate a histogram chart. Each bar of the histogram will represent a unique value in the column `StatusMachine` and the height of the bar will be the count of the occurrences of that value.
|
|
1293
|
+
|
|
1294
|
+
- Model: `EmbeddedDatabase1`
|
|
1295
|
+
- Query: `SELECT StatusMachine, COUNT(*) AS Occurrences FROM Machine_state WHERE StatusMachine >= 0 GROUP BY StatusMachine ORDER BY Occurrences DESC`
|
|
1296
|
+
- Label: `{Item}/StatusMachine`
|
|
1297
|
+
- Value: `{Item}/Occurrences`
|