I am trying to update a column based on the rownumber. Table Unpaid1 contain multiple transactions for alot of accounts. When i execute the following script, I often end up transactions With incorrect sequential number (Cycleage) like shown below. The cycleage should be in the order 1 to 4 from oldest to newest. What might be the cause of this issue? Is there another way to assign the cycleage?
Begin
For X in (Select * From temp_Unpaid1 order by trxnserno asc)
loop
Update temp_Unpaid1 t set t.cycleage = rownum where t.custid=x.custid;
end loop;
End;
+-----------+-----------+--------+--------+----------+
| TRXNSERNO | CUSTID | AMOUNT | DATE | CYCLEAGE |
+-----------+-----------+--------+--------+----------+
| 66 | 45 | -10 | 08.jan | 2 |
| 67 | 45 | -20 | 10.jan | 1 |
| 90 | 45 | -30 | 15.jan | 3 |
| 155 | 45 | -15 | 20.jan | 4 |
+-----------+-----------+--------+--------+----------+
The catch is that the ROWNUM in the update is independent of the SELECT statement and, in fact, it looks like you are doing unnecessary updates. Here's why:
The SELECT will return all rows in the order of TRXNSERNO and even if you have only the one custid, you'll get 4 rows with the same custid:
CUSTID TRXNSERNO ...(other columns)
45 66
45 67
45 90
45 155
Then for each of these rows, the update statement will run (so you'll be updating the same 4 rows 4 times.)
When the UPDATE runs, it finds the 4 rows for custid 45 (it doesn't know/care what the TRXNSERNO was in the SELECT) and updates them in whatever order it finds them. (There's nothing in the UPDATE statement that forces the order of the rows as they are updated.)
So, whatever order the (4th and last) update found them in is how the ROWNUM values will be assigned so if Oracle found them in the order of
CUSTID TRXNSERNO
45 67
45 66
45 90
45 155
Then the ROWNUM value will be in that order:
CUSTID TRXNSERNO ROWNUM
45 67 1
45 66 2
45 90 3
45 155 4
In another run (depending on circumstances) Oracle might execute the update and find the custid 45 rows in a completely different order like the following and assign them rownums accordingly:
CUSTID TRXNSERNO ROWNUM
45 155 1
45 66 2
45 90 3
45 67 4
You could keep your current code but just generate the ROWNUMs in the SELECT list and apply the value in the update to the custid/trxnserno pairs:
Begin
For X in
(Select tmp.*
, row_number() over (partition by tmp.custid order by tmp.trxnserno) as rn
From temp_Unpaid1 tmp)
loop
Update temp_Unpaid1 t
set t.cycleage = rn
where t.custid=x.custid
and t.trxnserno = x.trxnserno;
end loop;
End;
Alternatively, you could try this in a single SQL statement using something like MERGE and using only the WHEN MATCHED portion of it:
MERGE INTO temp_unpaid1 temp
USING
(Select t.*
, row_number() over (partition by custid order by trxnserno) as rn
From temp_Unpaid1
) rn_qry
ON (temp.custid = rn_qry.custid
AND temp.trxnserno = rn_qry.trxnserno
)
WHEN MATCHED THEN
UPDATE SET
temp.cyclage = rn_qry.rn
;
Note: unfortunately I can't test the accuracy of the syntax right now, but hopefully this is of some value. :)
Related
I have a SQLite database where entries are sorted like this:
| ID | length | breadth | height | time |
1 10 20 30 123
1 10 20 15 432
2 4 2 7 543
2 4 2 8 234
As you see, the height column can vary over time. I want to get the entry with the largest height, for every unique ID in my database. Is there some way to do this in one single query, instead of looping through all id's with something like this
for x in ids:
SELECT length, breadth, height FROM table WHERE id = x ORDER BY height DESC LIMIT 1
Use GROUP BY:
SELECT ID, MAX(height) FROM table GROUP BY ID
I have two tables
tmp_CID_EIDs:
EID
====
1
2
3
5
EID_PID:
EID PID
==========
1 99
2 99
3 88
5 99
12 55
18 66
I use the following query to get a list of all positions where EID matches in both tables:
SELECT EID,
PID
FROM EID_PID
WHERE EID IN temp_CID_EIDs
-->
EID PID
=========
1 99
2 99
3 88
5 99
But my final goal is to get the number of unique PIDs from this query.
--> 99
88
How can I do that? Thanks..
SELECT DISTINCT PID FROM EID_PID WHERE EID IN tmp_CID_EIDs;
For example we have this 2 tables
table1:
Dir_num | dir_name
10 | john
11 | vlad
table2:
game | dir_num
101 | 10
111 | 10
102 | 11
104 | 10
104 | 10
Now I try to find the dir_name who has less than 2 games played.
select * from table1 where dir_num ....
Please help me find pl/sql code.
The output should show
Vlad.
Try something like this -- use GROUP BY and HAVING:
SELECT t.dir_num, t.dir_name
FROM Table1 t
LEFT JOIN Table2 t2 ON t.dir_num = t2.dir_num
GROUP BY t.dir_num, t.dir_name
HAVING COUNT(DISTINCT t2.game) < 2
And here is the SQL Fiddle.
If you want those in both tables (meaning at least 1 game), then change LEFT JOIN to INNER JOIN.
Good luck.
Please try:
SELECT t.dir_num, t.dir_name
FROM Table1 t
INNER JOIN Table2 t2 ON t.dir_num = t2.dir_num
GROUP BY t.dir_num, t.dir_name
HAVING COUNT(DISTINCT t2.game) < 2
I am hoping to get some help on a view which needs to be pivoted, I am not sure though.
View is in following format:
CASE CASE_ORDER MANAGER MONTHLY_CASES FISCAL_CASES
case_1 1 John 15 84
case_1 1 Jeff 10 80
case_2 2 John 20 90
case_2 2 Jeff 13 65
case_3 3 John 7 72
case_3 3 Jeff 17 70
My final chart should look like the following:
CASE CASE_ORDER JOHN_CURR_MONTH JOHN_FY JOHN_CURR_MONTH JOHN_FY
case_1 1 15 84 10 80
case_2 2 20 90 13 65
case_3 3 7 72 17 70
My problem is that managers can change and so does the number of managers from month to month,
so I can't hard code their names (ie. 'mgr1' and 'mgr2') and use DECODE. It has to be dynamic...
Thanks for your suggestion, I figured it out. In fact there is a similar answer in tom kyte's blog (http://www.oracle.com/technetwork/issue-archive/2012/12-jul/o42asktom-1653097.html) which I modified for my purpose. Here it is:
CREATE OR REPLACE PROCEDURE dynamic_pivot_proc ( p_cursor IN OUT SYS_REFCURSOR )
AS
l_query LONG := 'SELECT case_order, case';
BEGIN
FOR x IN (SELECT DISTINCT manager FROM test_table ORDER BY 1 )
LOOP
l_query := l_query ||
REPLACE( q'|, MAX(DECODE(manager,'$X$',monthly_total)) $X$_current_month|',
'$X$', dbms_assert.simple_sql_name(x.manager) ) ||
REPLACE( q'|, MAX(DECODE(manager,'$X$',fiscal_total)) $X$_fy|',
'$X$', dbms_assert.simple_sql_name(x.manager) );
END LOOP;
l_query := l_query || ' FROM test_table
GROUP BY case_order, case
ORDER BY case_order ';
OPEN p_cursor FOR l_query;
END;
SQL> variable x refcursor;
SQL> exec dynamic_pivot_proc( :x );
SQL> print x
CASE CASE_ORDER JEFF_CURRENT_MONTH JEFF_FY JOHN_CURRENT_MONTH JOHN_FY
1 case_1 10 80 15 84
2 case_2 13 65 20 90
3 case_3 17 70 7 72
Now the thing is instead of printing the result I want to store it in a view. How do I achieve that? I tried to modify the line
l_query LONG := 'SELECT case_order, case';
with
l_query LONG := 'CREATE OR REPLACE VIEW SELECT case_order, case';
Needless to say that it did not work because CREATE OR REPLACE is a DDL statement, so some how I have to use EXECUTE IMMEDIATE.
Any suggestion? Thanks in advance.
I am using oracle 11g and have written a stored procedure which stores values in temporary table as follows:
id count hour age range
-------------------------------------
0 5 10 61 10-200
1 6 20 61 10-200
2 7 15 61 10-200
5 9 5 61 201-300
7 10 25 61 201-300
0 5 10 62 10-20
1 6 20 62 10-20
2 7 15 62 10-20
5 9 5 62 21-30
1 8 6 62 21-30
7 10 25 62 21-30
10 15 30 62 31-40
now using this temp table i want to return two cursors. one for 61 and one for 62(age).
and for cursors there distinct range will be columns . for example cursor for age 62 should return following as dataset.
user 10-20 21-30 31-40
Count/hour count/hour count/hour
----------------------------------------------
0 5 10 - - - -
1 6 20 8 6 - -
2 7 15 - - - -
5 - - 9 5 - -
7 - - 10 25 - -
10 - - - - 15 30
this column range in temp table is is not a fixed values these are referenced from other table.
edited: i am using PIVOT for above problem, all examples i saw in internet are there for fixed values of column values (range in my case). how can i get dynamic values. following is the ex query:
SELECT *
FROM (SELECT column_2, column_1
FROM test_table)
PIVOT (SUM(column1) AS sum_values FOR (column_2) IN ('value1' AS a, 'value2' AS b, 'value3' AS c));
Instead of using handwritten value i am using following query inside 'IN'
SELECT * from(
with x as (
SELECT DISTINCT range
FROM test_table
WHERE age = 62 )
select ltrim( max( sys_connect_by_path(range, ','))
keep (dense_rank last order by curr),
',') range
from (select range,
row_number() over (order by range) as curr,
row_number() over (order by range) -1 as prev
from x)
connect by prev = PRIOR curr
start with curr = 1 )
it is giving error in this case. But when i using handwritten values its giving right output.
select * from (select user_id, nvl(count,0) count, nvl(hour,0) hour,nvl(range,0) range,nvl(age,0)
age from test_table)
PIVOT (SUM(count) as sum_count, sum(hour) as sum_hour for (range) IN
(
'10-20','21-30','31-40'
)
) where age = 62 order by userid
how can i give values dynamically there?
how can i do it.
Cursors are slow, I would recommend trying to do this in a query unless there's no alternative (or speed doesn't matter). You may want to look into: PIVOT / UNPIVOT which can rotate columns (in this case "range").
Here's some PIVOT / UNPIVOT documentation and examples:
http://www.oracle-developer.net/display.php?id=506
Based on your last edit:
Pretty sure you have two options:
Build dynamic sql based on the distinct values found in the "range" column.
You'll probably be stuck using a cursor again to build the column names but at least it will be limited to just the distinct ranges.
Oracle has a PIVOT XML command that you can use for this.
See: http://www.oracle.com/technetwork/articles/sql/11g-pivot-097235.html
And scroll down to the section: "XML Type"