There is a table1 with fields call_id, param0, param1, param2, ...param30 .
The param fields take values from 1 to 100.
And there is a second table table2 with fields call_id, theme_code
which Ineed to fill out from the first table,
The complexity of the task is that for one call_id I need to take each of these param as theme_code,
And if one of the param is null, then you don't need to create a new record for call_id
Example:
table1:
callid | par0 | par1 | par2 | par3 | par4 | par5 | par6 | par7 | par8 | par9 | par10 |
-------------------------------------------------------------------------------------------
1234567 | 24 | 2 | null | 91 | 58 | null | 25 | 19 | 77 | 62 | null |
table2:
callid | theme_code |
------------------------
1234567 | 24 |
------------------------
1234567 | 2 |
------------------------
1234567 | 91 |
------------------------
1234567 | 58 |
------------------------
1234567 | 25 |
------------------------
1234567 | 19 |
------------------------
1234567 | 77 |
------------------------
1234567 | 62 |
You seem to just want to unpivot the columns from table1 into multiple rows:
select callid, theme_code
from table1
unpivot (
theme_code
for par in (par0, par1, par2, par3, par4, par5, par6, par7, par8, par9, par10)
)
CALLID
THEME_CODE
1234567
24
1234567
2
1234567
91
1234567
58
1234567
25
1234567
19
1234567
77
1234567
62
fiddle
You can read more about pivoting and unpivoting in this article.
Unless table1 is some kind of staging table, copying that data into table2 would go against normalisation principles, and you might be better off just using that query when you need it, or creating a view based on it (which could be a materialised view).
If you really do want to put the unpivoted data into another table then you can use that query as the basis for an insert or merge statement.
Related
I have 2 source tables at the moment.
Table #1: sourceTableMain
|EmployeeNumber| DepartmentNumber | CostCenterNumber |
| -------------| ---------------- |------------------|
| 1 | 100 | 1001 |
| 2 | 200 | 1001 |
| 3 | 100 | 1002 |
Table #2: sourceTableEmployee
|EmployeeNumber| EmployeeFirstName | EmployeeLastName | EmployeeAddress |
| -------------| ---------------- |------------------|---------------- |
| 1 | Michael | Scott | 110 ABC Ln |
| 1 | Michael | Scott | 450 XYZ Ln |
| 2 | Dwight | Schrute | 321 PQR St |
| 3 | Jim | Halpert | 678 LMN Blvd |
I am trying to insert the combine the rows into a 3rd table named targetTableCombined which has the following schema:
FieldName
Type
Mode
employeeNumber
INTEGER
NULLABLE
employeeDetails
(struct)
RECORD
REPEATED
employeeFirstName
STRING
NULLABLE
employeeLastName
STRING
NULLABLE
employeeAddress
STRING
NULLABLE
Within the target table (targetTableCombined), I am trying to make sure that for each employeeNumber, all of the First Names, Last Names and Addresses are repeated under a single struct array. For example, EmployeeNumber 1 should have only 1 row in the target table, with the first name, last name and different addresses as part of the second column (struct), each in a separate row.
I wrote an insert script to do this, but I am going wrong:
insert into `dev.try_sbx.targetTableCombined`
select
main.employeeNumber,
array(
select as struct
emp.employeeFirstName,
emp.employeeLastName,
emp.employeeAddress
)
from
`dev.try_sbx.sourceTableMain` as main
inner join `dev.try_sbx.sourceTableEmployee` as emp
on main.EmployeeNumber = emp.EmployeeNumber;
This is the result I am getting when running the query above:
| EmployeeNumber | EmployeeDetails |
| ------------- | ------------------------------ |
| 1 | [Michael, Scott, 110 ABC Ln] |
| 1 | [Michael, Scott, 450 XYZ Ln] |
| 2 | [Dwight, Schrute, 321 PQR St] |
| 3 | [Jim, Halpert, 678 LMN Blvd] |
(Sorry about not being able to share screenshots - I don't have enough rep. But to elaborate, I am expecting only 3 rows on the insert (employee 1 should have had a single array containing both addresses). I am instead, getting 4 rows after the insert.)
Where am I going wrong with my script?
It's because ARRAY() is not an aggregation function. You should ARRAY_AGG() along with GROUP BY to group details for each employee into an array.
SELECT EmployeeNumber,
ARRAY_AGG((SELECT AS STRUCT EmployeeFirstName, EmployeeLastName, EmployeeAddress)) AS employeeDetails
FROM `dev.try_sbx.sourceTableEmployee`
GROUP BY 1;
More preferred way is :
SELECT EmployeeNumber,
ARRAY_AGG(STRUCT(EmployeeFirstName, EmployeeLastName, EmployeeAddress)) AS employeeDetails
FROM `dev.try_sbx.sourceTableEmployee`
GROUP BY 1;
output:
Consider the following scenario. I have a Customer table, which includes RowStart and EndDate logic, thus writing a new row every time a field value is updated.
Relevant fields in this table are:
RowStartDate
RowEndDate
CustomerNumber
EmployeeFlag
For this, I'd like to write a query, which will return an employee's period of tenure (EmploymentStartDate, and EmploymentEndDate). I.e. The RowStartDate when EmployeeFlag first became 'Y', and then the first RowStartDate where EmployeeFlag changed to 'N' (Ordered of course, by the RowStartDate asc). There is an additional complexity in that the Flag value may change between Y and N multiple times for a single person, as they may become staff, resign and then be employed again at a later date.
Example table structure is:
| CustomerNo | StaffFlag | RowStartDate | RowEndDate |
| ---------- | --------- | ------------ | ---------- |
| 12 | N | 2019-01-01 | 2019-01-14 |
| 12 | N | 2019-01-14 | 2019-03-02 |
| 12 | Y | 2019-03-02 | 2019-10-12 |
| 01 | Y | 2020-03-13 | NULL |
| 12 | N | 2019-10-12 | 2020-01-01 |
| 12 | Y | 2020-01-01 | NULL |
Output could be something like
| CustomerNo | StaffStartDate | StaffEndDate |
| ---------- | -------------- | ------------ |
| 12 | 2019-03-02 | 2019-10-12 |
| 01 | 2020-03-13 | NULL |
| 12 | 2021-01-01 | NULL |
Any ideas on how I might be able to solve this would be really appreciated.
Make sure you order the columns by ID and by dates:
select *
from yourtable
order by CustomerNumber asc,
EmployeeFlag desc,
RowStartDate asc,
RowEndDate asc
This gives you a list of all changes over time per employee.
Subsequently, you want to map two rows into a single row with two columns (two dates mapped into overall start and end date). Others have done this using the lead() function. For details please have a look here: Merging every two rows of data in a column in SQL Server
I got rather complicated riddle to solve. So far I'm unlocky.
I got 3 tables which I need to join to get the result.
Most important is that I need highest h_id per p_id. h_id is uniqe entry in log history. And I need newest one for given point (p_id -> num).
Apart from that I need ext and name as well.
history
+----------------+---------+--------+
| h_id | p_id | str_id |
+----------------+---------+--------+
| 1 | 1 | 11 |
| 2 | 5 | 15 |
| 3 | 5 | 23 |
| 4 | 1 | 62 |
+----------------+---------+--------+
point
+----------------+---------+
| p_id | num |
+----------------+---------+
| 1 | 4564 |
| 5 | 3453 |
+----------------+---------+
street
+----------------+---------+-------------+
| str_id | ext | name |
+----------------+---------+-------------+
| 15 | | Mein st. 33 | - bad name
| 11 | | eck st. 42 | - bad name
| 62 | abc | Main st. 33 |
| 23 | efg | Back st. 42 |
+----------------+---------+-------------+
EXPECTED RESULT
+----------------+---------+-------------+-----+
| num | ext | name |h_id |
+----------------+---------+-------------+-----+
| 3453 | efg | Back st. 42 | 3 |
| 4564 | abc | Main st. 33 | 4 |
+----------------+---------+-------------+-----+
I'm using Oracle SQL. Tried using query below but result is not true.
SELECT num, max(name), max(ext), MAX(h_id) maxm FROM history
INNER JOIN street on street.str_id = history._str_id
INNER JOIN point on point.p_id = history.p_id
GROUP BY point.num
In Oracle, you can use keep:
SELECT p.num,
MAX(h.h_id) as maxm,
MAX(s.name) KEEP (DENSE_RANK FIRST ORDER BY h.h_id DESC) as name,
MAX(s.ext) KEEP (DENSE_RANK FIRST ORDER BY h.h_id DESC) as ext
FROM history h INNER JOIN
street s
ON s.str_id = h._str_id INNER JOIN
point p
ON p.p_id = h.p_id
GROUP BY p.num;
The keep syntax allows you to do "first()" and "last()" for aggregations.
I'm trying to compare two sets of data in the same table to verify an update operation. I've created this query to view the different sets of information side-by-side, but when I add an additional constraint in the WHERE clause, I get zero rows returned.
The following query shows me the two record sets next to each other, so I can kinda eyeball that there are different componentids:
WITH src AS
(SELECT id AS s_id,
moduleid AS s_moduleid,
instanceid AS s_instanceid,
tagid AS s_tagid,
componentid AS s_componentid
FROM component_junction WHERE id=103)
SELECT * FROM component_junction cj
JOIN src ON s_moduleid=cj.moduleid
AND s_instanceid=cj.instanceid
AND s_tagid=cj.tagid
WHERE cj.id=117
Returns:
id | moduleid | instanceid | tagid | componentid | s_id | s_moduleid | s_instanceid | s_tagid | s_componentid
----|----------|------------|-------|-------------|------|------------|--------------|---------|--------------
117 | 2923 | 7179 | 1 | <null> | 103 | 2923 | 7179 | 1 | <null>
117 | 2923 | 7179 | 2 | <null> | 103 | 2923 | 7179 | 2 | <null>
117 | 2924 | 1404 | 1 | <null> | 103 | 2924 | 1404 | 1 | <null>
117 | 2924 | 1404 | 2 | <null> | 103 | 2924 | 1404 | 2 | <null>
117 | 1 | 41 | 2 | <null> | 103 | 1 | 41 | 2 | 267
117 | 1 | 40 | 2 | <null> | 103 | 1 | 40 | 2 | 267
117 | 1 | 38 | 2 | <null> | 103 | 1 | 38 | 2 | 267
But the below query does not return me any rows. Note the extra AND clause at the end:
WITH src AS
(SELECT id AS s_id,
moduleid AS s_moduleid,
instanceid AS s_instanceid,
tagid AS s_tagid,
componentid AS s_componentid
FROM component_junction WHERE id=103)
SELECT * FROM component_junction cj
JOIN src ON s_moduleid=cj.moduleid
AND s_instanceid=cj.instanceid
AND s_tagid=cj.tagid
WHERE cj.id=117 AND s_componentid != cj.componentid;
I know the values are different since I can see it in the result set from the first query. Some NULL values are present in both sets for componentid so I'd expect those not to show in the second query.
One or both values appear to be NULL. Postgres supports the ANSI standard NULL safe comparison, so change
s_componentid != cj.componentid
to:
s_componentid is distinct from cj.componentid
As one NULL value differs from another NULL value, you may use ISNULL function for ComponentID column like below:
WITH src AS
(SELECT id AS s_id,
moduleid AS s_moduleid,
instanceid AS s_instanceid,
tagid AS s_tagid,
ISNULL(componentid,'') AS s_componentid
FROM component_junction WHERE id=103)
SELECT * FROM component_junction cj
JOIN src ON s_moduleid=cj.moduleid
AND s_instanceid=cj.instanceid
AND s_tagid=cj.tagid
WHERE cj.id=117
AND ISNULL(s_componentid,'') != ISNULL(cj.componentid,'');
I am wanting to add a extra 2 rows to my table for each part number which is present. Currently I have something like this:
+-------------+-----------+---------------+
| item_number | operation | resource_code |
+-------------+-----------+---------------+
| abc | 10 | kit |
| abc | 20 | build |
| abc | 30 | test |
+-------------+-----------+---------------+
There are hundreds of more items set up like this within the table. I am wanting to add 2 extra lines of records to the table based upon each part number. So once these have been added my data set will look like this:
+-------------+-----------+---------------+
| item_number | operation | resource_code |
+-------------+-----------+---------------+
| abc | 10 | kit |
| abc | 20 | build |
| abc | 30 | test |
| abc | NULL | NULL |
| abc | NULL | NULL |
+-------------+-----------+---------------+
I am wanting these new records to be blank for now and add to them later.
I am using access and looking for the sql to add these new records to the table.
Try this on for size:
INSERT INTO my_table
SELECT item_number, NULL AS operation, NULL AS resource_code
FROM my_table
GROUP BY item_number
UNION ALL
SELECT item_number, NULL AS operation, NULL AS resource_code
FROM my_table
GROUP BY item_number