pl sql update rows with value from the same table - sql

In agent table I have field bossId which is an Id of other agent,
so I generated data for all other fields in agent table and now I need to set bossId for all the rows.
So each row need to select agentId for its bossId field from the same table.
there can be some agents with the same boss but agent cant be boss of himself.
this is the table I have
+---------+------+--------+
| agentId | name | bossId |
+---------+------+--------+
| 123 | aaa | |
| 124 | bbb | |
| 125 | ccc | |
| 126 | ddd | |
+---------+------+--------+
wanted resault:
+---------+------+--------+
| agentId | name | bossId |
+---------+------+--------+
| 123 | aaa | 124 |
| 124 | bbb | 123 |
| 125 | ccc | 126 |
| 126 | ddd | 123 |
+---------+------+--------+
so the empty column bossId needs to be filled with AgentId of the same table
how to do it in pl sql?
UPDATE:
tried using this code which seems to be ok but I get errors
begin
for i in 1..17 loop
update policeman p
set bossid = (select p2.officerid
from policeman p2
order by dbms_random.value
where rownum = 1)
where rownum =i;
end loop;
end ;
error:
ORA-06550: line 6, column 18:
PL/SQL: ORA-00907: missing right parenthesis
ORA-06550: line 3, column 5:
PL/SQL: SQL Statement ignored

Does this do what you want?
update t
set bossid = (select max(bossid) keep (dense_rank first order by dbms_random.random)
from t
);
Note: This may not be very efficient if your data is not small.

This should work provided agentid is unique.
update t tgt
set bossid = (select max(agentid)
from t src
where tgt.agentid<>src.agentid);

Related

How do I insert multiple rows from one table into a struct column of a single row of another table?

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:

SQL SELECT most recently created row WHERE something is true

I am trying to SELECT the most recently created row, WHERE the ID field in the row is a certain number, so I don't want the most recently created row in the WHOLE table, but the most recently created one WHERE the ID field is a specific number.
My Table:
Table:
| name | value | num |SecondName| Date |
| James | HEX124 | 1 | Carl | 11022020 |
| Jack | JEU836 | 4 | Smith | 19042020 |
| Mandy | GER234 | 33 | Jones | 09042020 |
| Mandy | HER575 | 7 | Jones | 10052020 |
| Jack | JEU836 | 4 | Smith | 14022020 |
| Ryan | GER631 | 33 | Jacque | 12042020 |
| Sarah | HER575 | 7 | Barlow | 01022019 |
| Jack | JEU836 | 4 | Smith | 14042020 |
| Ryan | HUH233 | 33 | Jacque | 15042020 |
| Sarah | HER575 | 7 | Barlow | 02022019 |
My SQL:
SELECT name, value, num, SecondName, Date
FROM MyTable
INNER JOIN (SELECT NAME, MAX(DATE) AS MaxTime FROM MyTable GROUP BY NAME) grouped ON grouped.NAME = NAME
WHERE NUM = 33
AND grouped.MaxTime = Date
What I'm doing here, is selecting the table, and creating an INNER JOIN where I'm taking the MAX Date value (the biggest/newest value), and grouping by the Name, so this will return the newest created row, for each person (Name), WHERE the NUM field is equal to 33.
Results:
| Ryan | HUH233 | 33 | Jacque | 15042020 |
As you can see, it is returning one row, as there are 3 rows with the NUM value of 33, two of which are with the Name 'Ryan', so it is grouping by the Name, and returning the latest entry for Ryan (This works fine).
But, Mandy is missing, as you can see in my first table, she has two entries, one under the NUM value of 33, and the other with the NUM value of 7. Because the entry with the NUM value of 7 was created most recently, my query where I say 'grouped.MaxTime = Date' is taking that row, and it is not being displayed, as the NUM value is not 33.
What I want to do, is read every row WHERE the NUM field is 33, THEN select the Maximum Time inside of the rows with the value of 33.
I believe what it is doing, prioritising the Maximum Date value first, then filtering the selected fields with the NUM value of 33.
Desired Results:
| Ryan | HUH233 | 33 | Jacque | 15042020 |
| Mandy | GER234 | 33 | Jones | 09042020 |
Any help would be appreciated, thank you.
If I folow you correctly, you can filter with a subquery:
select t.*
from mytable t
where t.num = 33 and t.date = (
select max(t1.date) from mytable t1 where t1.name = t.name and t1.num = t.num
)
Look at your subquery. You want the maximum dates for num 33, but you are selecting the maximum dates independent from num.
I think you want:
select *
from mytable
where (name, date) in
(
select name, max(date)
from mytable
where num = 33
group by name
);

SQL - joining 3 tables and choosing newest logged entry per id

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.

SQL Join Two Columns In Same Table

I need to get records that are not used. My tables go like this:
segment table
| id | name |
-------------
| 1 | AAA |
| 2 | BBB |
| 3 | CCC |
| 4 | DDD |
segment_segment_assoc
| id | segment_name | child_name |
----------------------------------
| 1 | AAA | BBB |
| 2 | AAA | CCC |
The segment_segment_assoc table stores the parent/child relationships between the segments. I need a query that will be able to a count of how many segments in the segment table are not in the the segment_segment_assoc table.
The way I want to do this is somehow get the segment_segment_assoc table to transform to a single name type table. In example, it should turn to this =>
| name |
--------
| AAA |
| BBB |
| CCC |
Where the segment_segment_assoc.segment_name and segment_segment_assoc.child_name columns are "appended" or "joined" together. Once I have that, I could then do a "NOT IN" to see which segments are not being "used" in the segment_segment_assoc table.
This should be written in SQL but just for context, I am using Postgres.
Thanks in advanced!
Using UNION should do the trick:
SELECT count(*) FROM segment
WHERE name NOT IN
(
SELECT segment_name FROM segment_segment_assoc
UNION
SELECT child_name FROM segment_segment_assoc
)

Fetch latest record inserted by SYSTEM

I have a requirement where I need to fetch the latest record persisted by 'SYSTEM' even it has been modified by multiple users. Request you to help me in building that query.
Case-1 data:
uniqueId-111 --> This record has been inserted by system twice(records-1,4) and the same has been updated by multiple users(records-2,3,5). Now I need a query when I pass Id=111 and Type=I I should get the latest record inserted by 'SYSTEM' i.e record-4
Case-2 data:
uniqueId-222 --> This record has been inserted by system once(records-6) and the same has been updated by multiple users(records-7,9).When I pass Id=222 and Type=I I should get the latest record inserted by 'SYSTEM' i.e record-6
Test Data:
PK | ID | TYPE | USER | Date | OtherInfo
---------------------------------------------------------------
1 | 111 | I | SYSTEM | 01/Aug | A
2 | 111 | I | XYZ | 02/Aug | B
3 | 111 | I | ABC | 03/Aug | C
4 | 111 | I | SYSTEM | 04/Aug | D
5 | 111 | I | ABC | 05/Aug | E
6 | 222 | I | SYSTEM | 02/Aug | F
7 | 222 | I | PQR | 03/Aug | G
8 | 333 | C | XYZ | 03/Aug | H
9 | 222 | I | ABC | 04/Aug | I
Thanks in advance
RK
You need to select with your type, user and id and then order it by the date DESC. The Result you have to limit to 1
SELECT * FROM (
SELECT * FROM yourTable WHERE id = 111 AND type = 'I' AND user = 'SYSTEM' ORDER BY date_ DESC
) qry WHERE ROWNUM = 1;
See sqlfiddle: http://sqlfiddle.com/#!4/16b37/2