last time a table was accessed in oracle - sql

Is it possible to determine when the last time a table was accessed in Oracle?
I am trying this, but this will not include when was the last time the table was selected.
select * from dba_objects;

select p.object_owner owners, p.object_name Obj_Name, p.operation Operation,
p.options Options, count(1) Idx_Usg_Cnt
from dba_hist_sql_plan p,dba_hist_sqlstat s
where p.object_owner = '&USERNAME' and p.operation like 'TABLE%'
and p.sql_id = s.sql_id and p.object_name=’&OBJNAME’
group by p.object_owner,p.object_name,p.operation,p.options order by 1,2,3

From #KD 's answer I've developed this query to detect unused tables:
select a.obj#, b.object_name, b.owner, b.object_type, b.timestamp
from dba_hist_seg_stat a, dba_objects b
where a.obj# = b.object_id
and b.owner = 'YOUR_SCHEMA_NAME'
and object_name = 'A_TABLE_NAME'
order by timestamp desc
Since this query makes use of SYS user views, you must have special privileges to run it. If you have them, you can have a look at dba_hist_XXX views, or only have a look at the rest of the columns in the views used here: you have info about reads, writes, locks and many more.
EDIT: Thanks to #APC for the warning. DBA_HIST_XXX are views from Diagnostics Pack that require special license.

I'm using the SQL below to find the list of segments getting full table scaned and how many times
they have been full table scanned..is this SQL is correct with respect to this.
select a.obj#,a.table_scans_delta,b.object_name,b.owner,b.object_type
from dba_hist_seg_stat a, dba_objects b
where a.obj# = b.object_id
and b.owner like 'USERNAMES%'
order by table_scans_total desc

Related

How inaccurate can the sys.dm_db_partition_stats.row_count be in getting an Azure SQL DB row count for each table?

I have seen a number of general statements on how sys.dm_db_partition_stats.row_count can produce inaccurate results due to providing objects' statistics instead of actually doing a COUNT(). However, I have never been able to find any deeper reasons behind those statements or validate the hypothesis on my Azure SQL DB.
So I would like to learn -
How inaccurate this method can actually be?
Why exactly the results might be skewed? (e.g. stats are only recalculated once per day / on specific object operation).
Any related insight is much appreciated !
Several things I was able to find out on my own -- mostly by running various queries containing sys.dm_db_partition_stats.row_count, while knowing actual row counts in each table.
Here's a final query I came up with
This gets fast and (in my case) accurate row count for each table, sorted from high count to low.
SELECT
(SCHEMA_NAME(A.schema_id) + '.' + A.Name) as table_name,
B.object_id, B.index_id, B.row_count
FROM
sys.dm_db_partition_stats B
LEFT JOIN
sys.objects A
ON A.object_id = B.object_id
WHERE
SCHEMA_NAME(A.schema_id) <> 'sys'
AND (B.index_id = '0' OR B.index_id = '1')
ORDER BY
B.row_count DESC
First line of WHERE clause is used to exclude system tables, e.g. sys.plan_persist_wait_stats and many others.
Second line takes care of non-unique non-clustered indexes (which are objects and apparently have their own stats) -> if you don't filter them out, you get double row count for indexed tables when using GROUP BY A.schema_id, A.Name or two records with the same table_name in the query output (if you don't use GROUP BY)
We're glad that you found the solution and solved it by yourself. Your new edition should be an answer. I just help you post it as answer and this can be beneficial to other community members:
Several things I was able to find out on my own -- mostly by running various queries containing sys.dm_db_partition_stats.row_count, while knowing actual row counts in each table.
Here's a final query I came up with
This gets fast and (in my case) accurate row count for each table, sorted from high count to low.
SELECT
(SCHEMA_NAME(A.schema_id) + '.' + A.Name) as table_name,
B.object_id, B.index_id, B.row_count
FROM
sys.dm_db_partition_stats B
LEFT JOIN
sys.objects A
ON A.object_id = B.object_id
WHERE
SCHEMA_NAME(A.schema_id) <> 'sys'
AND (B.index_id = '0' OR B.index_id = '1')
ORDER BY
B.row_count DESC
First line of WHERE clause is used to exclude system tables, e.g. sys.plan_persist_wait_stats and many others.
Second line takes care of non-unique non-clustered indexes (which are objects and apparently have their own stats) -> if you don't filter them out, you get double row count for indexed tables when using GROUP BY A.schema_id, A.Name or two records with the same table_name in the query output (if you don't use GROUP BY)
Thanks for your sharing again.
And thanks for #conor's commnet: "If you want to see how far off the numbers can be, I suggest you try doing user transactions, inserting a bunch of rows, then roll back the transaction."

Query very long to execute

I am executing a Postgresql database on a 2GB RAM VPS.
The settings are :
max_connections = 100
work_mem=1MB
shared_buffers=128MB
I am executing a pretty simple query with a million rows :
SELECT s.executionTime, g.date, s.name
FROM SimulationStatsGroup g
LEFT JOIN SimulationStats s ON s.group_id = g.id
WHERE g.name = 'general'
ORDER BY g.date DESC
I have 2 tables : SimulationStatsGroup and SimulationStats. SimulationStatsGroup contains between 1 to 13 SimulationStats. SimulationStats is a simple entity that contains numeric values like executionTime used by my application. Each SimulationStatsGroup and SimulationStats have a name.
Here is the EXPLAIN ANALYZE that I get : http://explain.depesz.com/s/auLK
Why is my query so long to execute ?
Create indexes on SimulationStats(group_id) and SimulationStatsGroup(id).
In the Sort (step #2) in the explain plan, it looks like the database is either lugging around unreferenced columns (not optimum) and/or sorting by them (ouch). Honestly though, I don't work on Postgres, so that is just an educated guess. The database engine may not be smart enough to discard unreferenced columns early in the process. I'd try this SQL to nudge the database engine into discarding unreferenced columns before it gets to the sort, and you may see a significant runtime improvement:
SELECT s.executionTime, g.date, s.name
FROM ( select id, date from SimulationStatsGroup WHERE g.name = 'general') as g
LEFT JOIN ( select s.group_id, s.name, s.executionTime from SimulationStats ) as s
ON s.group_id = g.id
ORDER BY g.date DESC
If this version shows a runtime improvement, please run another Explain, and let us know if there are less columns list in the Sort step. If so, my hunch was likely correct. If correct, hopefully the Postgres developers will take note and try to discard unreferenced columns for us in a future version, instead of us manually coding it.

Oracle 11g : Meta data query very slow

I have this view that should display comments and constraints (including check conditions where applicable) for the columns of some tables in a schema.
Essentially I'm (left ) joining ALL_COL_COMMENTS to ALL_CONS_COLUMNS to ALL_CONSTRAINTS.
However, this is really slow for some reason ( takes around 10 seconds ) even though I have a very small number of tables ( just 7 ) , very small number of columns ( 58 columns in total ). So the query returns few results. And it's still slow. What can I do ?
CREATE OR REPLACE FORCE VIEW "MYDB"."COMMENTS_VIEW" ("TABLE_NAME", "COLUMN_NAME", "COMMENTS", "CONSTRAINT_TYPE", "CHECK_CONDITION") AS
SELECT r.TABLE_NAME, r.COLUMN_NAME, r.COMMENTS, DECODE(q.CONSTRAINT_TYPE,'P', 'Primary Key', 'C', 'Check Constraint', 'R', 'Referential Integrity Constraint' ), q.SEARCH_CONDITION AS CHECK_CONDITION
FROM ALL_COL_COMMENTS r -- ALL_COL_COMMENTS has the COMMENTS
LEFT JOIN ALL_CONS_COLUMNS p ON (p.TABLE_NAME = r.TABLE_NAME AND p.OWNER = 'MYDB' AND p.COLUMN_NAME = r.COLUMN_NAME) -- ALL_CONS_COLUMNS links COLUMNS to CONSTRAINTS
LEFT JOIN ALL_CONSTRAINTS q ON (q.OWNER = 'MYDB' AND q.CONSTRAINT_NAME = p.CONSTRAINT_NAME AND q.TABLE_NAME = p.TABLE_NAME AND (q.CONSTRAINT_TYPE = 'C' OR q.CONSTRAINT_TYPE = 'P' OR q.CONSTRAINT_TYPE = 'R' ) ) -- this gives us INFO on CONSTRAINTS
WHERE r.OWNER = 'MYDB'
AND
r.TABLE_NAME IN ('TABLE1', 'TABLE2', 'TABLE3', 'TABLE4', 'TABLE5', 'TABLE6', 'TABLE7')
AND
r.COLUMN_NAME NOT IN ('CREATED', 'MODIFIED', 'CREATED_BY', 'MODIFIED_BY')
ORDER BY r.TABLE_NAME, r.COLUMN_NAME, r.COMMENTS;
Ensure the dictionary and fixed object statistics are up-to-date. Checking for up-to-date statistics is a good first step for almost any SQL performance problem. The dictionary and fixed objects are unusual, and there's a good chance nobody has considered gathering statistics on them before.
begin
dbms_stats.gather_fixed_objects_stats;
dbms_stats.gather_dictionary_stats;
end;
/
Try to join on table, and column ids instead of names where possible. Even OWNER if you can. Example:
ON p.TABLE_ID = r.TABLE_ID
Also, you are selecting from objects that are already views of who knows how many underlying tables. The query optimizer is probably having a hard time (and maybe giving up in some aspects). Try to translate your query into using the base tables.
I would either use a query profiler, or (simpler) just remove parts of your query until it gets super fast. For example, remove the DECODE() call, maybe that's doing it.

Oracle Join View - which rowid is used

CREATE VIEW EVENT_LOCATION ("EVENT_ID", "STREET", "TOWN") AS
SELECT A.EVENT_ID, A.STREET, A.TOWN
FROM TBLEVENTLOCATION A
JOIN TBLEVENTS B
ON A.EVENT_ID = B.EVENT_ID
WHERE B.REGION = 'South';
if I run
SELECT ROWID, STREET, TOWN FROM EVENT_LOCATION
then which ROWID should I get back?
Reason I'm asking is:
In the database there are many views with the above 'pattern'. It seems to differ which rowid is being returned from different views. ie. I am getting both A.ROWID or B.ROWID ...
UPDATE:
I have resolved this using the following view. Which essentially guarantees the ROWID comes from the right table. Thanks for your replies!
CREATE VIEW EVENT_LOCATION ("EVENT_ID", "STREET", "TOWN") AS
SELECT A.EVENT_ID, A.STREET, A.TOWN
FROM TBLEVENTLOCATION A
WHERE A.EVENT_ID IN (SELECT EVENT_ID FROM TBLEVENTS WHERE REGION = 'South');
Try looking at
select * from user_updatable_columns where table_name = 'EVENT_LOCATION'
The columns that are updatable should indicate the table (and hence the rowid) which Oracle says is the child.
Bear in mind that, if you use multi-table clusters (not common, but possible), then different tables in the same cluster can have records with the same ROWID.
Personally, I'd recommend (a) don't use ROWID in your code anywhere and (b) if you do, then include an explicit evt.rowid evt_rowid column in the view.
Since you get ORA-01445 if non of the tables you use are key-preserving I think it will return the rowid of one of the key-preserving tables. I don't know what will happen if several tables are key-preserving.

COUNT in a query with multiple JOINS and a GROUP BY CLAUSE

I am working on a database that contains 3 tables:
A list of companies
A table of the products they sell
A table of prices they offered on each date
I'm doing a query like this in my php to generate a list of the companies offering the lowest prices on a certain product type on a certain date.
SELECT
a.name AS company,
c.id,
MIN(c.price) AS apy
FROM `companies` a
JOIN `company_products` b ON b.company_id = a.id
JOIN `product_prices` c ON c.product_id = b.id
WHERE
b.type = "%s"
AND c.date = "%s"
GROUP BY a.id
ORDER BY c.price ASC
LIMIT %d, %d
This gets me the data I need, but in order to implement a pager in PHP I need to know how many companies offering that product on that day there are in total. The LIMIT means that I only see the first few...
I tried changing the SELECT clause to SELECT COUNT(a.id) or SELECT COUNT(DISTINCT(a.id)) but neither of those seem to give me what I want. I tried removing the GROUP BY and ORDER BY in my count query, but that didn't work either. Any ideas?
Looks to me like you should GROUP BY a.id, c.id -- grouping by a.id only means you'll typically have several c.ids per a.id, and you're just getting a "random-ish" one of them. This seems like a question of basic correctness. Once you have fixed that, an initial SELECT COUNT(*) FROM etc etc should then definitely give you the number of rows the following query will return, so you can prepare your pager accordingly.
This website suggests MySQL has a special trick for this, at least as of version 4:
Luckily since MySQL 4.0.0 you can use SQL_CALC_FOUND_ROWS option in your query which will tell MySQL to count total number of rows disregarding LIMIT clause. You still need to execute a second query in order to retrieve row count, but it’s a simple query and not as complex as your query which retrieved the data.
Usage is pretty simple. In you main query you need to add SQL_CALC_FOUND_ROWS option just after SELECT and in second query you need to use FOUND_ROWS() function to get total number of rows. Queries would look like this:
SELECT SQL_CALC_FOUND_ROWS name, email
FROM users
WHERE name LIKE 'a%'
LIMIT 10;
SELECT FOUND_ROWS();
The only limitation is that you must call second query immediately after the first one because SQL_CALC_FOUND_ROWS does not save number of rows anywhere.