oracle admin datafiles tablespace - ghdrako/doc_snipets GitHub Wiki

SELECT tablespace_name,
       contents,
       extent_management,
       segment_space_management
FROM dba_tablespaces;
Kolumna Co oznacza
CONTENTS Typ przestrzeni: 🔹 PERMANENT – zwykła przestrzeń dla danych 🔹 TEMPORARY – dla sortowania, operacji tymczasowych 🔹 UNDO – do zarządzania transakcjami (rollback, undo)
EXTENT_MANAGEMENT Zarządzanie extentami: 🔹 LOCAL – lokalnie zarządzana (zalecana) 🔹 DICTIONARY – starszy typ, zarządzany przez katalog (niezalecany)
SEGMENT_SPACE_MANAGEMENT Zarządzanie segmentami:🔹 AUTO – automatyczne (zalecane)🔹 MANUAL – ręczne zarządzanie (starsze wersje)

Przykład wyniku:

TABLESPACE_NAME CONTENTS EXTENT_MANAGEMENT SEGMENT_SPACE_MANAGEMENT
USERS PERMANENT LOCAL AUTO
TEMP TEMPORARY LOCAL N/A
UNDO UNDO LOCAL N/A
SELECT file#, name FROM v$datafile;
     FILE# NAME
---------- -------------------------------------
         1 /u01/oracle/oradata/MITP/system01.dbf
         3 /u01/oracle/oradata/MITP/sysaux01.dbf
         4 /u01/oracle/oradata/MITP/undotbs01.dbf
         5 /u01/oracle/oradata/MITP/tools01.dbf
         7 /u01/oracle/oradata/MITP/users01.dbf

Lista plikow danej przestrzeni tabel

SELECT tablespace_name, file_name
FROM dba_data_files
WHERE tablespace_name = 'USERS';  -- wpisz nazwę swojej przestrzeni

Przeniesienie pliku do innej lokalizacji Od Oracle 12c (12.1 i nowsze) możesz użyć polecenia:

ALTER DATABASE MOVE DATAFILE 5 TO '/u02/oracle/oradata/MITP/tools01.dbf';

i wykonać przeniesienie online, bez odmontowywa i bez restartu bazy, o ile plik nie należy do systemowych przestrzeni tabel (np. SYSTEM, SYSAUX) i nie jest w użyciu w sposób, który to blokuje.


SQL> ALTER DATABASE MOVE DATAFILE 5 TO
  2  '/u02/oracle/oradata/MITP/tools01.dbf';

SQL> SELECT file#, name FROM v$datafile;
     FILE# NAME
---------- -------------------------------------
         5 /u02/oracle/oradata/MITP/tools01.dbf

Po wykonaniu polecenia FLASHBACKDATABASE plik danych nie jest przenoszony do swojej pierwotnej lokalizacji. Dane zostaną jednak zaktualizowane do starego statusu

SELECT tablespace_name,file_name
  2  FROM dba_data_files
  3  ORDER BY 1,2;

Istnieją dwa podstawowe typy przestrzeni tabel:

  • Locally Managed Tablespaces
  • Dictionary Managed Tablespaces

Domyślnym typem jest Lokalnie zarządzana przestrzeń tabelowa. Wolną przestrzenią zarządza się za pośrednictwem mapy bitowej w nagłówku pliku danych. Segmentami w przestrzeni tabel można zarządzać automatycznie lub ręcznie. Standardem jest automatyczne zarządzanie przestrzenią segmentów (ASSM), z wyjątkiem przestrzeni tabel SYSTEM, UNDO i TEMP. Oprócz lepszej wydajności, ASSM oferuje zaletę wyeliminowania potrzeby ręcznego zarządzania parametrami pamięci masowej, co upraszcza administrację. Przestrzeń tabel zarządzana za pomocą słownika wykorzystuje katalog bazy danych do zarządzania zakresami. Był to domyślny typ w poprzednich wersjach i został zastąpiony lokalnie zarządzaną przestrzenią tabel w celu uzyskania lepszej wydajności i uniknięcia konfliktów w katalogu.

Przestrzenie tabel zarządzane przez słownik nadal mogą być używane ze względów zgodności.

W wersji 10g wprowadzono kolejny typ przestrzeni tabel:Bigfile Tablespace . Było to spowodowane głównie rosnącym rozmiarem baz danych. Plik danych małej przestrzeni tabelowej plików (domyślnie) może składać się maksymalnie z 4 milionów bloków danych. Przy rozmiarze bloku 8 KB oznacza to, że maksymalny rozmiar pliku danych nie może przekroczyć 32 GB. Plik danych dużej przestrzeni tabelowej plików może osiągnąć rozmiar 32 TB przy rozmiarze bloku 8 KB. Jednak w przestrzeni tabel dużego pliku nie może znajdować się więcej niż jeden plik danych.

Pliki dziennika redo tworzą dziennik transakcji bazy danych. Zmiany w bazie danych zwykle nie są zapisywane na dysku po zakończeniu transakcji (COMMIT), lecz pozostają w pamięci podręcznej bufora bazy danych aż do następnego punktu kontrolnego. Są one jednak zapisywane w plikach dziennika redo, tak aby w przypadku awarii bazy danych możliwe było odzyskanie wszystkich zakończonych transakcji. Pliki dziennika powtórzeń online mają zatem kluczowe znaczenie dla możliwości odzyskania bazy danych. Pliki dziennika powtórzeń online składają się z kilku grup. Każda grupa może zawierać wielu członków (plików). Pliki członków grupy są identycznymi lustrzanymi odbiciami, co zapewnia niezawodność. Jeśli członek grupy ulegnie uszkodzeniu lub zostanie przypadkowo usunięty, dostęp do pozostałych członków będzie nadal możliwy. Zazwyczaj w skład grupy wchodzą dwie osoby

Log Writer zawsze zapisuje do dokładnie jednej grupy o statusie CURRENT,Jeśli grupa jest pełna, ma miejsce przełączenie dziennika. Logwriter zmienia się w następną grupę i oznacza je jako CURRENT. Gdy dziennik dotrze do ostatniej grupy, zaczyna się ponownie od pierwszego i zastępuje go. Jeśli baza danych działa w trybie Archivelog, grupa jest zarchiwizowana przed nadpisaniem. Kopia jest tworzona w postaci zarchiwizowanego pliku dziennika ponownego. Oracle gwarantuje zatem przywrócenie w dowolnym momencie z powodu poprzedniej kopii zapasowej. Nowy numer sekwencji jest tworzony z każdym przełącznikiem dziennika.


SQL> SELECT group#,sequence#,status,first_time
  2  FROM v$log;
    GROUP#  SEQUENCE# STATUS           FIRST_TIME
---------- ---------- ---------------- -------------------
         1         73 CURRENT          23.06.2019 20:00:51
         2         71 INACTIVE         20.06.2019 21:00:16
         3         72 INACTIVE         23.06.2019 18:49:32

Utworzenie przestrzeni tabel z autoextend

CREATE TABLESPACE moja_przestrzen
DATAFILE '/u01/app/oracle/oradata/moja_przestrzen01.dbf'
SIZE 500M
AUTOEXTEND ON
NEXT 100M
MAXSIZE 2G;

Ustawienie pliku jako autoextend

ALTER DATABASE DATAFILE '/ścieżka/do/plik_danych.dbf'
AUTOEXTEND ON
NEXT 100M
MAXSIZE 2G;
  • AUTOEXTEND ON – włącza automatyczne zwiększanie pliku danych, gdy kończy się miejsce.
  • NEXT 100M – ustala krok powiększania, czyli o ile ma rosnąć plik danych za każdym razem.
  • MAXSIZE 2G – ustala maksymalny rozmiar, do jakiego plik może urosnąć.

Gdy mamy ustawiony data guard to na standby:

ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;

-- Wykonaj zmianę, np. AUTOEXTEND:
ALTER DATABASE DATAFILE '/u02/oracle/oradata/xyz01.dbf' AUTOEXTEND ON NEXT 100M MAXSIZE 2G;

-- Wznów recovery w tle:
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;

Utworzenie przestrzeni tabel z autoextend

CREATE TABLESPACE moja_przestrzen
DATAFILE '/u01/app/oracle/oradata/moja_przestrzen01.dbf'
SIZE 500M
AUTOEXTEND ON
NEXT 100M
MAXSIZE 2G;

Włączyć auto-rozszerzanie (autoextend) na poziomie datafile’a

SELECT tablespace_name, file_name, autoextensible, bytes/1024/1024 AS size_mb
FROM dba_data_files;

Jeśli kolumna AUTOEXTENSIBLE ma wartość NO, to autoextend jest wyłączony.

Dodanie pliku

Rozmiar podawac w m - megabajty lug g - gigabyty

alter tablespace data add datafile '/ora/oradata/dip/data06.dbf' size 128m autoextend on next 128m maxsize 32765m;
ALTER DATABASE DATAFILE '/ścieżka/do/pliku.dbf'
AUTOEXTEND ON
NEXT 100M
MAXSIZE UNLIMITED;
ALTER DATABASE DATAFILE '/u01/app/oracle/oradata/ORCL/users01.dbf'
AUTOEXTEND ON
NEXT 50M
MAXSIZE 5G;

Opcje:

  • AUTOEXTEND ON – włącza auto-rozszerzanie
  • NEXT 100M – przyrost o 100 MB, gdy plik się zapełni
  • MAXSIZE UNLIMITED – brak ograniczenia rozmiaru (możesz też ustawić limit, np. MAXSIZE 10G)

Wyłaczenie autoextend

ALTER DATABASE DATAFILE '/ścieżka/do/pliku.dbf'
AUTOEXTEND OFF;

Ustawienie pliku jako autoextend

ALTER DATABASE DATAFILE '/ścieżka/do/plik_danych.dbf'
AUTOEXTEND ON
NEXT 100M
MAXSIZE 2G;
  • AUTOEXTEND ON – włącza automatyczne zwiększanie pliku danych, gdy kończy się miejsce.
  • NEXT 100M – ustala krok powiększania, czyli o ile ma rosnąć plik danych za każdym razem.
  • MAXSIZE 2G – ustala maksymalny rozmiar, do jakiego plik może urosnąć.

Gdy mamy ustawiony data guard to na standby:

ALTER DATABASE RECOVER MANAGED STANDBY DATABASE CANCEL;

-- Wykonaj zmianę, np. AUTOEXTEND:
ALTER DATABASE DATAFILE '/u02/oracle/oradata/xyz01.dbf' AUTOEXTEND ON NEXT 100M MAXSIZE 2G;

-- Wznów recovery w tle:
ALTER DATABASE RECOVER MANAGED STANDBY DATABASE DISCONNECT FROM SESSION;

Create Tablespace

Create tablespace test_tbs datafile '/u01/test_tbs_01.dbf' size 50m;

Add Space to Tablespace

!!! Make sure you have space at OS level before resizing or adding new datafile

Alter database datafile '/u01/test_tbs_01.dbf' resize 10g;
Alter tablespace test_tbs add datafile '/u01/test_tbs_02.dbf' size 5g;

Powieksz limit w autoextend

ALTER DATABASE DATAFILE '/ścieżka/do/pliku.dbf' AUTOEXTEND ON MAXSIZE 10G;

Dodanie pliku do tablespace z autoextend

alter tablespace sysaux add datafile '/ora/oradata/ora/sysaux02.dbf' size 64m autoextend on next 64m maxsize 32765m;

Drop Tablespace

drop tablespace test_tbs including contents and datafiles;
  • If INCLUDING CONTENTS is specified, all objects within the tablespace are deleted.
  • If INCLUDING DATAFILES is specified, physical files on disk are also removed.
  • Ensure there are no cross-tablespace references (e.g., indexes in another tablespace pointing to tables in the one being dropped).
  • Cannot drop an active undo tablespace; switch to a new undo tablespace first.
  • If CASCADE CONSTRAINTS - drop all referential integrity constraints from tables outside tablespace that refer to primary and unique keys of tables inside tablespace. If you omit this clause and such referential integrity constraints exist, then Oracle Database returns an error and does not drop the tablespace. Zeby wykonac to polecenie najpierw sprawdz
  • Żaden użytkownik nie ma test_tbs jako domyślnej przestrzeni
  • Nie istnieją żadne klucze obce (FOREIGN KEY) łączące obiekty w test_tbs z obiektami w innych tablespace’ach
  • (opcjonalnie) Jeśli używasz Data Guard i chcesz też usunąć pliki na standby, sprawdź STANDBY_FILE_MANAGEMENT=’AUTO’.
SELECT username
FROM   dba_users
WHERE  default_tablespace = 'RECLAIM_TS';

ALTER USER <nazwa_użytkownika> DEFAULT TABLESPACE users;
WITH fk AS (
  SELECT c.owner          AS child_owner,
         c.constraint_name,
         c.table_name      AS child_table,
         c.r_owner         AS parent_owner,
         c.r_constraint_name
  FROM   dba_constraints c
  WHERE  c.constraint_type = 'R'
)
SELECT fk.constraint_name,
       fk.child_owner     AS child_schema,
       fk.child_table     AS child_table,
       s1.tablespace_name AS child_ts,
       pc.table_name      AS parent_table,
       s2.tablespace_name AS parent_ts
FROM   fk
  JOIN dba_segments  s1 
    ON s1.owner        = fk.child_owner
   AND s1.segment_name = fk.child_table
  JOIN dba_constraints pc
    ON pc.owner        = fk.parent_owner
   AND pc.constraint_name = fk.r_constraint_name
  JOIN dba_segments  s2 
    ON s2.owner        = pc.owner
   AND s2.segment_name = pc.table_name
WHERE  (s1.tablespace_name = 'RECLAIM_TS' AND s2.tablespace_name <> 'RECLAIM_TS')
   OR (s1.tablespace_name <> 'RECLAIM_TS' AND s2.tablespace_name = 'RECLAIM_TS');

Jeśli to zapytanie zwróci wiersze, oznacza to, że jakieś relacje FK krzyżują się z twoją przestrzenią – najpierw musisz je usunąć lub zmodyfikować (ALTER TABLE … DROP CONSTRAINT … lub przenieść tabele do innej TS).

Połącz się do standby (np. w SQL*Plus jako SYSDBA):

SQL> SHOW PARAMETER standby_file_management;

SQL> SELECT name, value
     FROM   v$parameter
     WHERE  name = 'standby_file_management';

lub

DGMGRL> SHOW CONFIGURATION;

Tablespace Coalesce

  • Tablespace Coalesce combines all contiguous free extents into larger contiguous extents inside all datafiles
  • It takes any free extents that are right next to some other free extent and make one bigger free extent
  • SMON will perform this coalescing in the background but if you need it to happen right now, coalesce will do it
SQL> ALTER TABLESPACE USERS COALESCE;

Find Tablespace Utilization

W Oracle 12c Release 2 (12.2) i nowszych został wprowadzony wygodny widok, który od razu pokazuje wielkość i faktyczne użycie każdej przestrzeni tabel:

W środowisku Multitenant (CDB/PDB) analogiczny widok to: CDB_TABLESPACE_USAGE_METRICS – identyczne kolumny plus CON_ID, pozwalające rozróżnić kontenery.

SELECT ut.tablespace_name,
       ROUND(ut.used_space * ts.block_size/1024/1024,2)   AS used_mb,
       ROUND(ut.tablespace_size * ts.block_size/1024/1024,2) AS size_mb,
       ROUND(ut.used_percent,2)                            AS pct_used
FROM   dba_tablespace_usage_metrics ut
JOIN   dba_tablespaces             ts
  ON   ut.tablespace_name = ts.tablespace_name
ORDER  BY pct_used DESC;

ekwiwalent

SELECT df.tablespace_name,
       ROUND(SUM(df.bytes)/1024/1024,2)                            AS total_mb,
       ROUND((SUM(df.bytes) - NVL(SUM(fs.bytes),0)) /1024/1024,2)   AS used_mb,
       ROUND(NVL(SUM(fs.bytes),0) / SUM(df.bytes) *100,2)           AS pct_free
FROM   dba_data_files df
LEFT   JOIN dba_free_space fs
  ON   df.tablespace_name = fs.tablespace_name
GROUP  BY df.tablespace_name;
set colsep |
set linesize 100 pages 100 trimspool on numwidth 14 
col name format a25
col owner format a15 
col "Used (GB)" format a15
col "Free (GB)" format a15 
col "(Used) %" format a15 
col "Size (M)" format a15 
SELECT d.status "Status", d.tablespace_name "Name", 
       TO_CHAR(NVL(a.bytes / 1024 / 1024 /1024, 0),'99,999,990.90') "Size (GB)", 
       TO_CHAR(NVL(a.bytes - NVL(f.bytes, 0), 0)/1024/1024 /1024,'99999999.99') "Used (GB)", 
       TO_CHAR(NVL(f.bytes / 1024 / 1024 /1024, 0),'99,999,990.90') "Free (GB)", 
       TO_CHAR(NVL((a.bytes - NVL(f.bytes, 0)) / a.bytes * 100, 0), '990.00') "(Used) %"
 FROM sys.dba_tablespaces d, 
      (select tablespace_name, sum(bytes) bytes from dba_data_files group by tablespace_name) a, 
      (select tablespace_name, sum(bytes) bytes 
         from dba_free_space group by tablespace_name) f 
        WHERE d.tablespace_name = a.tablespace_name(+) 
          AND d.tablespace_name = f.tablespace_name(+) 
          AND NOT (d.extent_management like 'LOCAL' AND d.contents like 'TEMPORARY') 
UNION ALL 
SELECT d.status 
       "Status", d.tablespace_name "Name", 
       TO_CHAR(NVL(a.bytes / 1024 / 1024 /1024, 0),'99,999,990.90') "Size (GB)", 
       TO_CHAR(NVL(t.bytes,0)/1024/1024 /1024,'99999999.99') "Used (GB)",
       TO_CHAR(NVL((a.bytes -NVL(t.bytes, 0)) / 1024 / 1024 /1024, 0),'99,999,990.90') "Free (GB)", 
       TO_CHAR(NVL(t.bytes / a.bytes * 100, 0), '990.00') "(Used) %" 
 FROM sys.dba_tablespaces d, 
      (select tablespace_name, sum(bytes) bytes from dba_temp_files group by tablespace_name) a, 
      (select tablespace_name, sum(bytes_cached) bytes from v$temp_extent_pool group by tablespace_name) t 
 WHERE d.tablespace_name = a.tablespace_name(+) 
   AND d.tablespace_name = t.tablespace_name(+) 
   AND d.extent_management like 'LOCAL' 
   AND d.contents like 'TEMPORARY';

Uzycie tablespace (dla danych, nie undo/temp)

SELECT
    df.tablespace_name,
    ROUND(df.total_mb, 2) AS total_mb,
    ROUND(df.total_mb - NVL(fs.free_mb, 0), 2) AS used_mb,
    ROUND(NVL(fs.free_mb, 0), 2) AS free_mb,
    ROUND((df.total_mb - NVL(fs.free_mb, 0)) / df.total_mb * 100, 2) AS pct_used
FROM
    (SELECT tablespace_name, SUM(bytes) / 1024 / 1024 AS total_mb
     FROM dba_data_files
     GROUP BY tablespace_name) df
LEFT JOIN
    (SELECT tablespace_name, SUM(bytes) / 1024 / 1024 AS free_mb
     FROM dba_free_space
     GROUP BY tablespace_name) fs
ON df.tablespace_name = fs.tablespace_name
ORDER BY pct_used DESC;

Uzycie tablespace uwzgledniajac AUTOEXTEND (czyli potencjalny wzrost)

SELECT
    a.tablespace_name,
    ROUND(a.used_mb, 2) AS used_mb,
    ROUND(a.free_mb, 2) AS free_mb,
    ROUND(a.max_mb, 2) AS max_mb,
    ROUND((a.used_mb / a.max_mb) * 100, 2) AS pct_used
FROM (
    SELECT
        df.tablespace_name,
        SUM(df.bytes) / 1024 / 1024 AS max_mb,
        SUM(df.bytes - NVL(fs.bytes, 0)) / 1024 / 1024 AS used_mb,
        SUM(NVL(fs.bytes, 0)) / 1024 / 1024 AS free_mb
    FROM dba_data_files df
    LEFT JOIN dba_free_space fs
        ON df.file_id = fs.file_id
    GROUP BY df.tablespace_name
) a
ORDER BY pct_used DESC;

TEMP tablespace (bo tam dba_free_space nie działa)

SELECT
    tablespace_name,
    ROUND(SUM(bytes_used)/1024/1024, 2) AS used_mb,
    ROUND(SUM(bytes_free)/1024/1024, 2) AS free_mb,
    ROUND(SUM(bytes_used + bytes_free)/1024/1024, 2) AS total_mb,
    ROUND(SUM(bytes_used) / SUM(bytes_used + bytes_free) * 100, 2) AS pct_used
FROM v$temp_space_header
GROUP BY tablespace_name;

Monitor tablespace usage

cat sql/ts.sql
select df.tablespace_name "Tablespace",
totalusedspace "Used MB",
(df.totalspace - tu.totalusedspace) "Free MB",
df.totalspace "Total MB",
round(100 * ( (df.totalspace - tu.totalusedspace)/ df.totalspace))
"Pct. Free"
from
(select tablespace_name,
round(sum(bytes) / 1048576) TotalSpace
from dba_data_files
group by tablespace_name) df,
(select round(sum(bytes)/(1024*1024)) totalusedspace, tablespace_name
from dba_segments
group by tablespace_name) tu
where df.tablespace_name = tu.tablespace_name
/
$cat to.sql
prompt
prompt Tablespace: &&1
prompt Object size > &&2 MB
set pagesize 999
set verify off
col owner for a16
col segment_name for a30
col segment_type for a20
select owner, segment_name, segment_type, round(bytes/1048576) MB
from dba_segments
where tablespace_name=upper('&&1')
  and bytes > nvl(to_number('&&2'),0) * 1024 * 1204
order by bytes desc
/

Lista segmentow ktore zajmoja najwiecej

SELECT OWNER, SEGMENT_NAME, SEGMENT_TYPE, ROUND(BYTES/1024/1024, 2) AS SIZE_MB
FROM DBA_SEGMENTS
WHERE TABLESPACE_NAME = 'DIP_DATA'
ORDER BY SIZE_MB DESC
FETCH FIRST 30 ROWS ONLY;

Kto jest wlascicielem obiektoe

SELECT OWNER, COUNT(*) AS SEGMENT_COUNT,
       ROUND(SUM(BYTES)/1024/1024, 2) AS TOTAL_SIZE_MB
FROM DBA_SEGMENTS
WHERE TABLESPACE_NAME = 'DIP_DATA'
GROUP BY OWNER
ORDER BY TOTAL_SIZE_MB DESC;

Shrink segments

SHRINK SPACE działa tylko dla segmentów w tablespace z segment space management typu AUTO

SELECT tablespace_name, segment_space_management
FROM dba_tablespaces
WHERE tablespace_name = 'DIP_DATA';

Shrink tables

ALTER TABLE twoja_tabela ENABLE ROW MOVEMENT;
ALTER TABLE twoja_tabela SHRINK SPACE;

Shrink Space with Cascade

ALTER TABLE twoja_tabela ENABLE ROW MOVEMENT;
ALTER TABLE ali.deneme SHRINK SPACE CASCADE;

Adding the CASCADE option to the SHRINK SPACE command extends the shrink operation beyond just the table. It includes any associated indexes and dependent objects related to the table. This comprehensive shrink operation ensures that all related structures are optimized, providing a more thorough space reclamation.

Shrink indexes

ALTER INDEX twoj_indeks SHRINK SPACE;
ANALYZE INDEX idx_empid VALIDATE STRUCTURE;

SELECT name,height,lf_rows,lf_blks,del_lf_rows FROM INDEX_STATS;
   
  NAME          HEIGHT      LF_ROWS    LF_BLKS    DEL_LF_ROW
  ------------- ----------- ---------- ---------- ----------
  IDX_EMPID               2          1          3          6 
   
  1 row selected.

Rebuild index only if you see HEIGHT is above 4 and Deleted Leaf Row is less than 20. You can simply rebuild an index to reclaim space

ALTER INDEX idx_empid REBUILD ONLINE;

Tabela z LOB-em (BLOB, CLOB)

ALTER TABLE twoja_tabela MODIFY lob(kolumna_lob) (SHRINK SPACE);
ALTER TABLE table_name MODIFY LOB(lob_column) (SHRINK SPACE CASCADE);

Można też shrinkować cały LOB segment:

ALTER LOB (kolumna_lob) STORE AS (SHRINK SPACE);

Shrink tablespcase

SQL> alter tablespace users coalesce;
SQL> ALTER DATABASE DATAFILE 72 RESIZE 1G;
ALTER DATABASE DATAFILE 72 RESIZE 1G
*
ERROR at line 1:
ORA-03297: file contains used data beyond requested RESIZE value 

Objects belonging to a tablespace might reside under Recyclebin which does not allow you to shrink the datafile. We must remove the tablespace specific segments from recycle bin first

SQL> purge tablespace users;
SQL> ALTER DATABASE DATAFILE 72 RESIZE 1G;
ALTER DATABASE DATAFILE 72 RESIZE 1G
*
ERROR at line 1:
ORA-03297: file contains used data beyond requested RESIZE value 

PURGE TABLESPACE command only removes recyclebin segments belonging to the currently connected user. There might be other users who have deleted objects from the tablespace that reside in recyclebin. Its a good idea to purge recyclebin as sysdba

SQL purge recyclebin;
SQL> ALTER DATABASE DATAFILE 72 RESIZE 1G;
Database altered.

Reclaim Space from Undo Tablespace

Reclaiming space from UNDO tablespace is very simple. Create a new undo tablespace and drop the old one

CREATE UNDO TABLESPACE undo2 DATAFILE '/u01/orcl/undo02.dbf' SIZE 1G;
ALTER SYSTEM SET UNDO_TABLESPACE=undo2;
DROP TABLESPACE undo1 INCLUDING CONTENTS AND DATAFILES;