I have issued following statement in Oracle on a huge table (1.8 TB size):
alter table drop unused columns checkpoint
It has been running for almost 10 days now, crashing a couple of times due to lack of memory.
I resumed it with :
alter table drop columns continue
How can I track the progress and possibly get an estimate of the finish time?
I tried querying v$session_longops but there are no records for this session.
I know this is an old question, but given that nobody actually ever answered it, here's what I came up with when I ran into the same problem:
select v.*, to_char(100*"Processed"/"# of Blocks", '990D000000') "% Complete"
from (select sid, serial#, status, event, p1text, p1, p2text, p2, p3text, p3, x.segment_name, x.extent_id,
(select sum(case
when p2 not between block_id and block_id + blocks - 1 then blocks
else p2 - block_id
end)
from dba_extents x2
where x2.segment_name = x.segment_name
and extent_id <= x.extent_id) "Processed",
(select sum(blocks)
from dba_extents
where segment_name = x.segment_name) "# of Blocks"
from v$session s
left join dba_extents x on p1 = file_id and p2 between block_id and block_id + blocks - 1
where (sid, serial#) in ((15,40610))
) v
Basically, I'm taking the P2 value (Block #) from the wait event on the session doing the drop, and identifying where within the table's list of segments the process is at.
There is some work left to the reader here, as this assumes that
You know the session and serial#
That "db file sequential read" activities are, in fact sequential.
That the session isn't swapping between multiple files
That the tablespace the table resides in isn't split across multiple files
One could probably also only look at which extent the session was in compared to the total number of extents in the segment to give a rough estimate of progress as well...
Hopefully somebody else can find this helpful, even if only as a breadcrumb to building a better solution.
Cheers
Good query above. I modified the query a bit:
select
v.*,
Round(100*"Processed"/"# of Blocks", 2) "% Complete"
from (
select
sid,
serial#,
status,
event,
p1text,
p1,
p2text,
p2,
p3text,
p3,
x.segment_name,
x.partition_name,
x.extent_id,
( select
sum(
case
when p2.partition_position < p.partition_position
then x2.blocks
when ( x2.partition_name is null or
p2.partition_position = p.partition_position) and
x2.file_id < x.file_id
then x2.blocks
when ( x2.partition_name is null or
p2.partition_position = p.partition_position) and
x2.file_id = x.file_id and
x2.block_id < x.block_id
then x2.blocks
when ( x2.partition_name is null or
p2.partition_position = p.partition_position) and
x2.file_id = x.file_id and
x2.block_id = x.block_id and
s.p2 < x2.block_id + x2.blocks
then s.p2 - x2.block_id
end
)
from DBA_EXTENTS x2
left join DBA_TAB_PARTITIONS p2
on p2.table_owner = x2.owner and
p2.table_name = x2.segment_name and
p2.partition_name = x2.partition_name
where x2.segment_name = x.segment_name
) as "Processed",
( select
sum(x2.blocks)
from DBA_EXTENTS x2
where x2.segment_name = x.segment_name
) as "# of Blocks"
from V$SESSION s
left join DBA_EXTENTS x
on s.p1 = x.file_id and
s.p2 between x.block_id and x.block_id + x.blocks - 1
left join DBA_TAB_PARTITIONS p
on p.table_owner = x.owner and
p.table_name = x.segment_name and
p.partition_name = x.partition_name
where
--(sid, serial#) in ((84,23431)) or
s.event = 'db file scattered read'
) v
Related
So I'm trying to work through a problem thats a bit hard to explain and I can't expose any of the data I'm working with but what Im trying to get my head around is the error below when running the query below - I've renamed some of the tables / columns for sensitivity issues but the structure should be the same
"Error from Query Engine - Out of range for integer: Infinity"
WITH accounts AS (
SELECT t.user_id
FROM table_a t
WHERE t.type like '%Something%'
),
CTE AS (
SELECT
st.x_user_id,
ad.name as client_name,
sum(case when st.score_type = 'Agility' then st.score_value else 0 end) as score,
st.obs_date,
ROW_NUMBER() OVER (PARTITION BY st.x_user_id,ad.name ORDER BY st.obs_date) AS rn
FROM client_scores st
LEFT JOIN account_details ad on ad.client_id = st.x_user_id
INNER JOIN accounts on st.x_user_id = accounts.user_id
--WHERE st.x_user_id IN (101011115,101012219)
WHERE st.obs_date >= '2020-05-18'
group by 1,2,4
)
SELECT
c1.x_user_id,
c1.client_name,
c1.score,
c1.obs_date,
CAST(COALESCE (((c1.score - c2.score) * 1.0 / c2.score) * 100, 0) AS INT) AS score_diff
FROM CTE c1
LEFT JOIN CTE c2 on c1.x_user_id = c2.x_user_id and c1.client_name = c2.client_name and c1.rn = c2.rn +2
I know the query works for sure because when I get rid of the first CTE and hard code 2 id's into a where clause i commented out it returns the data I want. But I also need it to run based on the 1st CTE which has ~5k unique id's
Here is a sample output if i try with 2 id's:
Based on the above number of row returned per id I would expect it should return 5000 * 3 rows = 150000.
What could be causing the out of range for integer error?
This line is likely your problem:
CAST(COALESCE (((c1.score - c2.score) * 1.0 / c2.score) * 100, 0) AS INT) AS score_diff
When the value of c2.score is 0, 1.0/c2.score will be infinity and will not fit into an integer type that you’re trying to cast it into.
The reason it’s working for the two users in your example is that they don’t have a 0 value for c2.score.
You might be able to fix this by changing to:
CAST(COALESCE (((c1.score - c2.score) * 1.0 / NULLIF(c2.score, 0)) * 100, 0) AS INT) AS score_diff
I'd like to know how I could improve the performance of the below query, since it is taking way too long to run, after all, it returns millions of rows... I'm a dummy when it comes to SQL...
SELECT CIAM.EXTERNAL_ID,
(SELECT NEW_CHARGES / 100
FROM BI_OWNER.CMF_BALANCE
WHERE ( ACCOUNT_NO, BILL_REF_NO ) = (SELECT ACCOUNT_NO,
MAX(BILL_REF_NO)
FROM BI_OWNER.CMF_BALANCE
WHERE
ACCOUNT_NO = CIAM.ACCOUNT_NO
GROUP BY ACCOUNT_NO))
"AMOUNT LAST BILL",
(SELECT 'ACTIVE DISCOUNT'
|| ' '
|| CCK.AVAIL_PERIODS
|| '/'
|| CC.TOTAL_PERIODS
FROM BI_OWNER.CUSTOMER_CONTRACT_KEY CCK,
BI_OWNER.CUSTOMER_CONTRACT CC
WHERE CC.PARENT_ACCOUNT_NO = CIAM.ACCOUNT_NO
AND CC.END_DT IS NULL
AND EXISTS (SELECT 1
FROM CONTRACT_TYPES
WHERE CONTRACT_TYPE = CC.CONTRACT_TYPE
AND PLAN_ID_DISCOUNT IS NOT NULL
AND DURATION_UNITS = -3)
AND ROWNUM = 1
AND CCK.TRACKING_ID = CC.TRACKING_ID
AND CCK.TRACKING_ID_SERV = CC.TRACKING_ID_SERV) "DISCOUNT",
(SELECT CC.TOTAL_PERIODS
FROM BI_OWNER.CUSTOMER_CONTRACT_KEY CCK,
BI_OWNER.CUSTOMER_CONTRACT CC
WHERE CC.PARENT_ACCOUNT_NO = CIAM.ACCOUNT_NO
AND CC.END_DT IS NULL
AND EXISTS (SELECT 1
FROM CONTRACT_TYPES
WHERE CONTRACT_TYPE = CC.CONTRACT_TYPE
AND PLAN_ID_DISCOUNT IS NOT NULL
AND DURATION_UNITS = -3)
AND ROWNUM = 1
AND CCK.TRACKING_ID = CC.TRACKING_ID
AND CCK.TRACKING_ID_SERV = CC.TRACKING_ID_SERV) "CYCLE"
,
(SELECT SUM(BALANCE_DUE)
FROM BI_OWNER.CMF_BALANCE
WHERE ACCOUNT_NO = CIAM.ACCOUNT_NO
AND PPDD_DATE < TRUNC(SYSDATE))
"DEBT"
FROM BI_OWNER.CUSTOMER_ID_ACCT_MAP CIAM
WHERE EXTERNAL_ID_TYPE = 1
AND EXISTS (SELECT 1
FROM BI_OWNER.CMF
WHERE ACCOUNT_NO = CIAM.ACCOUNT_NO
AND PREV_CUTOFF_DATE > SYSDATE - 30)
I would recommend identifying the SQL id for the query then using the SQL Monitor Report as it will tell you exactly what the execution plan is and where the SQL is spending most of it's time.
A simple way to get the SQL Monitor Report from SQL*Plus follows:
spool c:\temp\SQL_Monitor_rpt.html
SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
alter session set "_with_subquery" = optimizer;
SELECT DBMS_SQLTUNE.report_sql_monitor(
sql_id => '&SQLID' ,
type => 'HTML',
report_level => 'ALL') AS report
FROM dual;
spool off
Basically, you need to know your table sizes and how to get the large tables to have data access via an index (e.g. index on columns found in the where clause).
Here is an initial stab and may provide significant improvement. Many of your queries were correlated subqueries being executed for every record. Instead, I tried to build pre-query aggregates per account number in the select from/join section. Query first, then I'll explain logic after.
SELECT
CIAM.EXTERNAL_ID,
CMF_BALANCE.New_Charges / 100.0 "AMOUNT LAST BILL",
CCKs.Discount,
CCKs.Cycle,
AcntLast30.SumBalance "DEBT"
FROM
(SELECT
CMF.Account_No,
max( Bal.Bill_Ref_No ) MaxBillRef,
sum( case when Bal.PPDD_Date < TRUNC(SYSDATE )
then Bal.Balance_Due else 0 end ) SumBalance
from
BI_OWNER.CMF
JOIN BI_OWNER.CMF_BALANCE BAL
on CMF.Account_No = Bal.Account_No
where
CMF.PREV_CUTOFF_DATE > SYSDATE - 30
group by
CMF.Account_No ) AcntLast30
JOIN BI_OWNER.CUSTOMER_ID_ACCT_MAP CIAM
on AcntLast30.Account_No = CIAM.Account_No
AND CIAM.EXTERNAL_ID_TYPE = 1
JOIN BI_OWNER.CMF_BALANCE
on AcntLast30.Account_No = CMFBalance.Account_No
AND AcntLast30.MaxBillRef = CMFBalance.Bill_Ref_No
JOIN
(select
CC.Parent_Account_No,
CC.TOTAL_PERIODS "CYCLE",
'ACTIVE DISCOUNT' || ' ' || CCK.AVAIL_PERIODS || '/' || CC.TOTAL_PERIODS "DISCOUNT"
FROM
BI_OWNER.CUSTOMER_CONTRACT CC
JOIN BI_OWNER.CUSTOMER_CONTRACT_KEY CCK
ON CC.TRACKING_ID = CCK.TRACKING_ID
AND CC.TRACKING_ID_SERV = CCK.TRACKING_ID_SERV
AND ROWNUM = 1
JOIN ( select distinct Contract_Type
FROM CONTRACT_TYPES
WHERE PLAN_ID_DISCOUNT IS NOT NULL
AND DURATION_UNITS = -3) CT
on CC.Contract_Type = CT.Contract_Type
WHERE
CC.END_DT IS NULL ) CCKs
on AcntLast30.Account_No = CCKs.Parent_Account_No
The initial "FROM" clause, I have a subquery because you appear to be only interested in accounts within the last 30 days. So, while I'm there, I am joining to your CMF_Balance table and getting the maximum Bill_Ref_No per account AND the sum of the balance when the PPDD_Date is less than the TRUNC(sysdate) which is your "DEBT" result column. So now we have the finite list of accounts you are interested in with the account, max bill on file and the balance due summed up.
(SELECT
CMF.Account_No,
max( Bal.Bill_Ref_No ) MaxBillRef,
sum( case when Bal.PPDD_Date < TRUNC(SYSDATE )
then Bal.Balance_Due else 0 end ) SumBalance
from
BI_OWNER.CMF
JOIN BI_OWNER.CMF_BALANCE BAL
on CMF.Account_No = Bal.Account_No
where
CMF.PREV_CUTOFF_DATE > SYSDATE - 30
group by
CMF.Account_No ) AcntLast30
Next, simple join to the CIAM table to only get accounts for External_ID_Type = 1. This too could have been merged into the query above for "AcntLast30" alias result.
JOIN BI_OWNER.CUSTOMER_ID_ACCT_MAP CIAM
on AcntLast30.Account_No = CIAM.Account_No
AND CIAM.EXTERNAL_ID_TYPE = 1
Now, since the "AcntLast30" query has the account and max bill reference we then join back to the CMF_Balance on the account and bill reference # once thus giving us the CMF_BALANCE.New_Charges / 100.0 "AMOUNT LAST BILL"
JOIN BI_OWNER.CMF_BALANCE
on AcntLast30.Account_No = CMFBalance.Account_No
AND AcntLast30.MaxBillRef = CMFBalance.Bill_Ref_No
Finally the subquery alias result "CCKs". Since the Discount and Cycle use the same query/subquery/exists, I just ran it once that qualified on the discounts types and pulled the Account_No for the JOIN condition. Now we have the Discount and Cycle values per account.
If you are returning so many rows, I believe the performance gained by grabbing these pre-query aggregates once up-front and joining to by the account will be much faster than that of each time individually subquerying at every row.
There was a reference to ROWNUM without any table/alias reference so I am not sure the impact of that one within the query.
Final note. For things like the discount that may not be applicable, you may need to change it to a LEFT JOIN, of which those values would show as NULL. But without knowing the extent of data, Cartesian products of 1:many entries in given tables, I think this will work well for you. For the most part it looked like everything was resulting in only one record qualified per account where higher importance on join (such as the max bill reference).
I know this seems like a similar question that has been answered before, and it may be. However, I have looked through the answers and they seem to all work along the table.x = sum (case when (x))) example. None of them seem to work with getting a sum inside the CASE function.
My script before attempting to create an update script is
SELECT
Item
,Loc
,MinDRPQty
,NetNeed
,CASE
WHEN (NetNeed/MinDRPQty) <= 1 THEN MinDRPQty --use existing multiplier if sum is less than multiplier
ELSE CEIL(NetNeed/MinDRPQty)*MinDRPQty --determine appropriate multiple, convert to int, and multiply
END as "ExpectedOrder"
FROM
(
select
s.Item as Item,
s.Loc as Loc,
p.MinDRPQty as MinDRPQty,
SUM (s.OH + s.UDC_ActualIT + s.UDC_CommitIT - s.UDC_AllCustOrd - s.UDC_ADJ_AvgDailyDmd* (p.DRPCovDur/1440) - s.UDC_SafetyStock) as NetNeed
from SKU s, SKUPlanningParam p
where s.Item = p.Item and s.Loc = p.Loc group by s.Item, s.Loc, p.MinDRPQty
)
I am looking to update a field called UDC_NetNeed. So I need the NetNeed from this statement. Any help would be greatly appreciated. If this has been answered before, and I have missed it, I apologize.
update SKU s
set UDC_NetNeed = (
SELECT
CASE
WHEN (NetNeed/nullif(MinDRPQty, 0)) <= 1 THEN MinDRPQty --use existing multiplier if sum is less than multiplier
ELSE CEIL(NetNeed/nullif(MinDRPQty, 0))*MinDRPQty --determine appropriate multiple, convert to int, and multiply
END
FROM
(
select
s.Item as Item,
s.Loc as Loc,
p.MinDRPQty as MinDRPQty,
SUM (s.OH + s.UDC_ActualIT + s.UDC_CommitIT - s.UDC_AllCustOrd - s.UDC_ADJ_AvgDailyDmd* (p.DRPCovDur/1440) - s.UDC_SafetyStock) as NetNeed
from SKU s, SKUPlanningParam p
where s.Item = p.Item and s.Loc = p.Loc group by s.Item, s.Loc, p.MinDRPQty
) a where a.Item = s.Item and a.Loc = s.Loc and ROWNUM = 1
);
It's not clear what you what to update, I suppose it's the SKU table
" and ROWNUM = 1" this condition is unclear and added in order to prevent multiple results in the correlated subquery.
Some info
select
s.Item as Item,
s.Loc as Loc,
p.MinDRPQty as MinDRPQty,
SUM (...)
...
group by s.Item, s.Loc, p.MinDRPQty
It means that result can be something like this
Row 1: item=1, loc=1, MinDRPQty=1, netNeed=100500
Row 2: item=1, loc=1, MinDRPQty=2, netNeed=500
Suppose there is one row with item=1, loc=1 in the table SKU.
When you make the update described above there is a problem: for row "item=1, loc=1" Oracle does't know which netNeed to choose (100500 or 500). That's why I put "and ROWNUM = 1" (any first row that was found). But I'm not sure if it makes sense in your case. Maybe you need some extra condition here!
I am trying to distinguish the physical servers uptime from virtual ones by looking at OS. I am able to pull out the result of the VMWare OS, however, I'd like to group the physical servers as one row.
Here is the code I have so far:
SELECT TOP (100) PERCENT Avg(dbo.tblserveruptime.uptime) AS Uptime,
Count(*) AS Total
FROM dbo.server
INNER JOIN dbo.tblserveruptime
ON dbo.server.name = dbo.tblserveruptime.name
WHERE ( dbo.server.status = N'production' )
AND ( dbo.server.server_env = N'prod' )
AND ( dbo.server.os_type <> N'vmware' )
GROUP BY dbo.tblserveruptime.month,
dbo.tblserveruptime.year
HAVING ( dbo.tblserveruptime.month = 4 )
AND ( dbo.tblserveruptime.year = 2013 )
UNION
SELECT TOP (100) PERCENT Avg(dbo.tblserveruptime.uptime) AS Uptime,
Count(*) AS Total
FROM dbo.server
INNER JOIN dbo.tblserveruptime
ON dbo.server.name = dbo.tblserveruptime.name
WHERE ( dbo.server.status = N'production' )
AND ( dbo.server.server_env = N'prod' )
AND ( dbo.server.os_type = N'vmware' )
GROUP BY dbo.tblserveruptime.month,
dbo.tblserveruptime.year
HAVING ( dbo.tblserveruptime.month = 4 )
AND ( dbo.tblserveruptime.year = 2013 )
Whatever field it is that gives you the physical server name, add that field to your GROUP BY statement and put it first. It will then group by Server, Month and Year (as you currently have it). My guess is you might want to swap Year and Month as it makes more sense to do it that way than by Month and then Year.
Okay, thanks to all who tried to help. I tried putting images of my before and after results, but I couldn't since I don't have 10 reputation points. Anyways, I think I got it. Here it is:
SELECT AVG(dbo.tblServerUptime.Uptime) AS Uptime,
CASE WHEN OS_TYPE = 'VMWare' THEN 'Virtual' ELSE 'Physical' END AS [Physical vs VM]
FROM dbo.Server INNER JOIN
dbo.tblServerUptime ON dbo.Server.NAME = dbo.tblServerUptime.NAME
GROUP BY CASE WHEN OS_TYPE = 'VMWare' THEN 'Virtual' ELSE 'Physical' END
I am trying to set up a report that queries for data within five minutes of each other. This way, I can recognize that one of my machines is down. This is what I have so far:
SELECT A_M_DEVICE.M_Device_ID,
A_M_DEVICE.Machine_Name,
A_M_DEVICE.IP_Address,
A_M_DEVICE.Device_Type,
A_M_DEVICE.M_Device_Status_ID,
A_M_DEVICE.Status_Date,
S_M_DEVICE_STATUS.M_Device_Status_Desc,
A_M_DEVICE.Domain_Name FROM MConsole.dbo.A_M_DEVICE A_M_DEVICE
INNER JOIN
MConsole.dbo.S_M_DEVICE_STATUS S_M_DEVICE_STATUS
ON (A_M_DEVICE.M_Device_Status_ID =
S_M_DEVICE_STATUS.M_Device_Status_ID) WHERE (A_M_DEVICE.Machine_Name IN
('DeVA',
'DevB',
))
AND (A_M_DEVICE.Status_Date = DateDiff (Second) <= 300)
Image not allowed since I am a newbie. Else I would have posted one.
Alright - looks like I have enough reputations for a screenshot!
Any help, as always, will be highly appreciated.
Thank you in advance.
Here's an approximation - check for "< 5" vs. "> 5" etc.
SELECT M_Device_ID,
Machine_Name,
IP_Address,
Device_Type,
M_Device_Status_ID,
M_Device_Status_Desc,
Domain_Name
FROM
( SELECT M_Device_ID,
Machine_Name,
IP_Address,
Device_Type,
M_Device_Status_ID,
M_Device_Status_Desc,
Domain_Name,
MAX(Status_Date)
FROM A_M_DEVICE
GROUP BY
M_Device_ID,
Machine_Name,
IP_Address,
Device_Type,
M_Device_Status_ID,
M_Device_Status_Desc,
Domain_Name
) AS a
JOIN A_M_DEVICE AS b
ON
a.Machine_Name = b.Machine_Name
AND a.IP_Address = b.IP_Address
AND a.Device_Type = b.Device_Type
AND a.M_Device_Status_ID = b.M_Device_Status_ID
AND a.M_Device_Status_Desc = b.M_Device_Status_Desc
AND a.Domain_Name = b.Domain_Name
WHERE
DATEDIFF(minute, a.Status_Date, b.Status_Date) < 5
Select the rows that have a status date that doesn't match the MAX status date:
SELECT A_M_DEVICE.M_Device_ID,
A_M_DEVICE.Machine_Name,
A_M_DEVICE.IP_Address,
A_M_DEVICE.Device_Type,
A_M_DEVICE.M_Device_Status_ID,
A_M_DEVICE.Status_Date,
S_M_DEVICE_STATUS.M_Device_Status_Desc,
A_M_DEVICE.Domain_Name FROM MConsole.dbo.A_M_DEVICE A_M_DEVICE
INNER JOIN
MConsole.dbo.S_M_DEVICE_STATUS S_M_DEVICE_STATUS
ON (A_M_DEVICE.M_Device_Status_ID =
S_M_DEVICE_STATUS.M_Device_Status_ID) WHERE (A_M_DEVICE.Machine_Name IN
('DeVA',
'DevB',
))
AND (A_M_DEVICE.Status_Date NOT IN (SELECT MAX(A_M_DEVICE.Status_Date) FROM A_M_DEVICE)
The date will be identical for all rows that represent machines that are operational - so eliminating those that match the MAX date leave records representing machines that haven't had a status update.