Oracle SQL: Difference between revisions
Appearance
mNo edit summary |
(No difference)
|
Latest revision as of 22:43, 30 October 2024
DB Admin
SQL*Plus
sqlplus sys/oracle@evdsmgw01.evd.int:1521/smgwa as sysdba --or export ORACLE_HOME=/app/oracle/product/12.1.0.2/ee export ORACLE_SID=emtectn /app/oracle/product/12.1.0.2/ee/bin/sqlplus / as sysdba -- on which DB am I? select instance_name from v$instance; --DB Start and Stop --connect sqlplus / as sysdba -- shutdown shutdown immediate; -- start startup;
DB objects
-- Oracle Parameter
SELECT * FROM v$parameter t ORDER BY 2 ASC;
SELECT * FROM v$parameter t WHERE t.name = 'db_block_size';
-- Oracle Versionsnummer
SELECT * FROM v$version;
-- nice dba views
dba_segments
dba_tables -- all_tables ist Nutzerabhängig und zeigt weniger Daten
dba_lobs
all_tab_comments
all_indexes
-- All Functions
select * from dba_objects
where object_type = 'FUNCTION'
--and lower(object_name) like 'fnc_get_initial_c%'
order by object_name;
-- Package Versions
SELECT 'PKG_IMPORT_VD' AS "Package",
PKG_IMPORT_VD.fnc_get_header_version AS "Header",
PKG_IMPORT_VD.fnc_get_body_version AS "Body"
FROM dual;
-- deactivated triggers
SELECT * FROM dba_triggers WHERE status != 'ENABLED';
-- List all grants to a Table
SELECT * FROM DBA_TAB_PRIVS WHERE TABLE_NAME = 'GTMP_FARZ_GERAETE_STATUS';
-- search for a table
SELECT object_name, owner
FROM dba_objects
WHERE object_type LIKE ('TABLE%')
AND LOWER(object_name) LIKE ('%hist%')
-- AND owner LIKE ('t%')
ORDER BY 1 ASC;
Invalid Objects
-- This script will display a list of Oracle invalid objects:
SELECT object_name, owner, object_type, status
FROM dba_objects
WHERE status != 'VALID'
ORDER BY owner, object_type;
-- Fix Invalid Objects
BEGIN
FOR r IN (SELECT x.owner, x.object_name, x.object_type, x.object_id
FROM dba_objects x
WHERE x.status = 'INVALID'
-- AND x.object_type != 'SYNONYM'
ORDER BY x.owner, x.object_name) LOOP
dbms_output.put('Validiere ' || r.owner || '.' || r.object_name || ' (' || r.object_type || ', ' ||
r.object_id || ') ... ');
dbms_utility.validate(object_id => r.object_id);
FOR r_erg IN (SELECT x.status FROM dba_objects x WHERE x.object_id = r.object_id) LOOP
dbms_output.put_line(r_erg.status);
END LOOP;
--EXIT;
END LOOP;
END;
/
-- Var 2 All Objects of OWNER
begin
dbms_utility.compile_schema('EC_TSI',false);
end;
/
Size
see Oracle Basic Compression for Table
-- Update Statistics (DBA_Tables etc)
EXEC DBMS_STATS.GATHER_TABLE_STATS('SCHEMA1','COMPANY');
-- or
EXEC DBMS_STATS.GATHER_SCHEMA_STATS('SCHEMA1');
-- or
exec DBMS_STATS.GATHER_DATABASE_STATS;
-- mehr Infos, aber Splittung durch Partitionierung / SubTables
SELECT segment_name, segment_type, bytes FROM dba_segments ORDER BY bytes DESC
-- Max Size pro Datei ermitteln
select file_name, maxbytes/1024/1024 Max_MB from dba_data_files;
-- Auto Extend datafile für alle Datefiles (größe ggf. jeweils anpassen?)
select 'alter database datafile ||name|| autoextend on next 100M maxsize unlimited;' from v$datafile ;
WITH wasted AS
(SELECT TABLESPACE_NAME, round(SUM(BYTES) / 1024 / 1024, 0) "MB wasted"
FROM DBA_FREE_SPACE
GROUP BY TABLESPACE_NAME)
SELECT df.tablespace_name,
df.file_name,
df.file_id,
round(df.bytes / 1024 / 1024, 0) AS "DF MB",
w."MB wasted"
FROM dba_data_files df
JOIN wasted w
ON w.TABLESPACE_NAME = df.TABLESPACE_NAME
ORDER BY 5 DESC;
Size Analyse
---Title: Overview SELECT t.*, s.*, f.* FROM ( SELECT ROUND(SUM(NUM_ROWS * AVG_ROW_LEN)/1024/1024/1024,0) AS "GB Tables netto" FROM DBA_TABLES ) t , ( SELECT ROUND(SUM(bytes)/1024/1024/1024,0) AS "GB Segments" FROM DBA_SEGMENTS s ) s , ( SELECT ROUND(SUM(bytes)/1024/1024/1024,0) AS "GB DataFiles" FROM DBA_DATA_FILES f ) f ; ---Title: SegmentTypes SELECT s.segment_type AS "Type" , ROUND(SUM (s.BYTES)/1024/1024/1024, 2) AS GB , COUNT(*) AS "Anz" FROM dba_segments s GROUP BY s.segment_type order by 2 desc ; ---Title: SegmentTypes TBS EC_ SELECT s.segment_type AS "Type" , ROUND(SUM (s.BYTES)/1024/1024/1024, 2) AS GB , COUNT(*) AS "Anz" FROM dba_segments s WHERE (s.TABLESPACE_NAME LIKE 'EC#_%' ESCAPE '#') GROUP BY s.segment_type order by 2 desc ; ---Title: SegmentTypes TBS EC_ + NULL SELECT s.segment_type AS "Type" , ROUND(SUM (s.BYTES)/1024/1024/1024, 2) AS GB , COUNT(*) AS "Anz" FROM dba_segments s WHERE (s.TABLESPACE_NAME LIKE 'EC#_%' ESCAPE '#' OR s.TABLESPACE_NAME IS NULL) -- null für partitionierte GROUP BY s.segment_type order by 2 desc ; ---Title: Ranking Segments SELECT s.SEGMENT_NAME AS "SEGMENT_NAME" , ROUND (SUM(s.BYTES) /1024/1024/1024, 2) AS GB FROM DBA_SEGMENTS s WHERE (s.TABLESPACE_NAME LIKE 'EC#_%' ESCAPE '#' OR s.TABLESPACE_NAME IS NULL) -- null für partitionierte GROUP BY (SEGMENT_NAME) HAVING SUM(s.BYTES)>1*1024*1024 --1 MB Grenze ORDER BY 2 DESC ; ---Title: Ranking Tablespaces SELECT t.tablespace_name , ROUND(SUM(t.BYTES/1024/1024/1024),0) AS GB FROM DBA_SEGMENTS t GROUP BY t.tablespace_name ORDER BY 2 DESC ; ---Title: Ranking DataFiles SELECT t.TABLESPACE_NAME, ROUND(SUM(t.BYTES/1024/1024/1024),0) AS GB FROM DBA_DATA_FILES t GROUP BY t.TABLESPACE_NAME ORDER BY 2 DESC ; ---Title: TSP-vs-DataFiles -- joined Table Space size and Data Files size SELECT u1.tablespace_name , u1."GB_TSP", u2."GB_DF", u2."GB_DF"-u1."GB_TSP" AS "Diff DF-TSP" FROM ( SELECT t.tablespace_name , ROUND(SUM(t.BYTES/1024/1024/1024),1) AS "GB_TSP" FROM DBA_SEGMENTS t GROUP BY t.tablespace_name) u1 JOIN ( SELECT t.tablespace_name , ROUND(SUM(t.BYTES/1024/1024/1024),1) AS "GB_DF" FROM DBA_DATA_FILES t GROUP BY t.tablespace_name) u2 ON u1.tablespace_name = u2.tablespace_name ORDER BY 4 DESC ; ---Title: Indizes SELECT s.segment_name "Index", ROUND(s.BYTES/1024/1024/1024,2) "GB" , i.table_name FROM dba_segments s LEFT OUTER JOIN all_indexes i ON i.index_name = s.segment_name WHERE segment_type LIKE 'INDEX%' AND s.BYTES> 1024*1024*1024 -- 1GB ORDER BY 2 DESC ; ---Title: IndizesPerTab SELECT i.table_name, ROUND(SUM(s.BYTES/1024/1024/1024),2) "GB" FROM dba_segments s LEFT OUTER JOIN all_indexes i ON i.index_name = s.segment_name WHERE segment_type LIKE 'INDEX%' GROUP BY i.TABLE_NAME HAVING SUM(s.BYTES) > 1024*1024*1024 --1GB ORDER BY 2 DESC ; ---Title: Lobs SELECT l.segment_name, l.table_name, l.column_name ,ROUND(s.bytes/1024/1024/1024, 2) AS "GB" FROM dba_lobs l JOIN dba_segments s ON (l.segment_name = s.segment_name) WHERE s.bytes > 0.1* 1024*1024*1024 ORDER BY 4 DESC ; ---Title: DoppelteTabNamen SELECT t.table_name, count(*) FROM dba_tables t group by t.table_name having count(*)>=2 ORDER BY 1 ; ---Title: Verteilung auf Tabellenpartitionen with DatenFilter as ( select t.table_owner, t.table_name, sum (t.num_rows) FROM DBA_TAB_PARTITIONS t where t.tablespace_name like 'EC#_%' escape '#' group by t.table_owner, t.table_name having sum (t.num_rows) > 0 ) SELECT table_owner, table_name, partition_name, partition_position, num_rows, blocks FROM DatenFilter t1 join DBA_TAB_PARTITIONS t using (table_owner, table_name) order by table_owner, table_name, partition_position ; ---Title: Verteilung auf Indexpartitionen with DatenFilter as ( select t.index_owner, t.index_name, sum (t.num_rows) FROM dba_ind_partitions t where t.tablespace_name like 'EC#_%' escape '#' group by t.index_owner, t.index_name having sum (t.num_rows) > 0 ) SELECT i.owner, i.TABLE_NAME, i.index_name, ip.partition_name, ip.partition_position, ip.num_rows FROM DatenFilter f join dba_indexes i on i.owner = f.index_owner and i.index_name = f.index_name join dba_ind_partitions ip on ip.index_owner = f.index_owner and ip.index_name = f.index_name order by i.owner, i.TABLE_NAME, i.index_name, ip.partition_position ; ---Title: Tablespaces mit Platz im Datafile (fragmentierte) SELECT TABLESPACE_NAME, round(SUM(BYTES) / 1024 / 1024 / 1024, 2) "GB wasted" FROM DBA_FREE_SPACE GROUP BY TABLESPACE_NAME ORDER BY 2 DESC;
Table Size Analysis
---Title: Ranking Tables V2 via qInteressanteTabs
WITH qSegments AS
(SELECT SEGMENT_NAME AS table_name, owner, SUM(bytes) AS bytes
FROM DBA_SEGMENTS
WHERE segment_type LIKE 'TABLE%' --TABLE and TABLE PARTITION
GROUP BY (SEGMENT_NAME, owner)),
qIndizes AS
(SELECT i.table_name, i.owner, SUM(s.bytes) AS bytes
FROM dba_indexes i
JOIN dba_segments s
ON (i.index_name = s.segment_name AND i.owner = s.owner)
GROUP BY i.table_name, i.owner),
qLobs AS
(SELECT l.table_name, l.owner, SUM(s.bytes) AS bytes
FROM dba_lobs l
JOIN dba_segments s
ON (l.segment_name = s.segment_name AND l.owner = s.owner) -- ja, l.segment_name hier!
GROUP BY l.table_name, l.owner),
qInteressanteTabs AS
(SELECT t.TABLE_NAME, t.OWNER
FROM dba_tables t
WHERE t.num_rows >= 1000000
UNION
SELECT t.TABLE_NAME, t.OWNER
FROM qSegments t
WHERE t.bytes >= 100 * 1024 * 1024
UNION
SELECT t.TABLE_NAME, t.OWNER
FROM qIndizes t
WHERE t.bytes >= 100 * 1024 * 1024
UNION
SELECT t.TABLE_NAME, t.OWNER
FROM qLobs t
WHERE t.bytes >= 100 * 1024 * 1024)
SELECT TABLE_NAME AS "Table" -- identifier "t." ungueltig bei nat join using
,
owner AS "Owner",
t.TABLESPACE_NAME AS "TablesSpace",
ROUND(t.NUM_ROWS / 1000000, 2) AS "MillRows",
ROUND(t.NUM_ROWS * t.AVG_ROW_LEN / 1024 / 1024 / 1024, 2) AS "GB Netto"
--, ROUND (t.blocks * 8 * 1024 /1024/1024/1024, 2) AS "GB Brutto"
,
ROUND(s.BYTES / 1024 / 1024 / 1024, 2) AS "GB Segments",
ROUND((s.BYTES - (t.NUM_ROWS * t.AVG_ROW_LEN)) / 1024 / 1024 / 1024, 2) AS "GB Wasted" -- t.NUM_ROWS * t.AVG_ROW_LEN
,
ROUND(Indizes.BYTES / 1024 / 1024 / 1024, 2) AS "GB Indizes",
ROUND(Lobs.BYTES / 1024 / 1024 / 1024, 2) AS "GB Lobs",
ROUND((s.BYTES + nvl(Indizes.bytes, 0) + nvl(Lobs.BYTES, 0)) / 1024 / 1024 / 1024, 2) AS "GB S+I+L"
, c.comments AS "TabComments"
, t.last_analyzed AS "last_analyzed"
FROM qInteressanteTabs
JOIN dba_tables t
USING (table_name, owner)
JOIN ( -- hier kein outer join!
SELECT TABLE_NAME, owner, bytes FROM qSegments) s
USING (TABLE_NAME, owner)
--
LEFT OUTER JOIN ( --Comments, hier kein "with", da so gefiltert wird
SELECT owner, table_name, comments FROM dba_tab_comments) c
USING (TABLE_NAME, owner)
LEFT OUTER JOIN ( --Indizes
SELECT table_name, owner, bytes FROM qIndizes) Indizes
USING (TABLE_NAME, owner)
LEFT OUTER JOIN ( --Lobs
SELECT table_name, owner, bytes FROM qLobs) Lobs
USING (TABLE_NAME, owner)
--WHERE (t.TABLESPACE_NAME LIKE /*EC,SC,CC_*/ '_C#_%' ESCAPE '#' OR t.TABLESPACE_NAME IS NULL) -- null fuer partitionierte, dieser Filter erst hier am Ende!
WHERE owner NOT IN ('SYS', 'XDB')
ORDER BY 10 DESC -- 10 = S+I+L
;
Groth
/*
Create two tables to monitor the growth of large tables vs. time.
prc_mon_table_growth is used to insert new data
*/
-- als sys/oracle
grant select on DBA_SEGMENTS to ec_mon; --ec_ewe
grant select on DBA_TABLES to ec_mon; --ec_ewe
grant select on DBA_SEGMENTS to ec_sys;
grant select on DBA_TABLES to ec_sys;
-- als ec_mon
-- Creates
CREATE TABLE RDS$TOMENKE$GROWTH1
(
SEGMENT_NAME VARCHAR2(81) NOT NULL,
OWNER VARCHAR2(30) NOT NULL,
DATE_INSERTED DATE NOT NULL,
BYTES NUMBER NOT NULL,
IGNORE VARCHAR2(1) NULL
);
ALTER TABLE RDS$TOMENKE$GROWTH1 ADD CONSTRAINT RDS$ToMenke$Growth1 UNIQUE (SEGMENT_NAME, OWNER, DATE_INSERTED);
COMMENT ON TABLE RDS$TOMENKE$GROWTH1 IS 'by Menke@RDS: Monitoring des Tab. Wachstums';
CREATE TABLE RDS$TOMENKE$GROWTH2
(
TABLE_NAME VARCHAR2(30) NOT NULL,
OWNER VARCHAR2(30) NOT NULL,
DATE_INSERTED DATE NOT NULL,
DATE_ANALYZED DATE NOT NULL,
NUM_ROWS NUMBER NOT NULL,
BYTESNETTO NUMBER NOT NULL,
BYTESBRUTTO NUMBER NOT NULL,
BYTESSEGMENTS NUMBER NOT NULL,
IGNORE VARCHAR2(1) NULL
);
ALTER TABLE RDS$TOMENKE$GROWTH2 ADD CONSTRAINT RDS$ToMenke$Growth2 UNIQUE (TABLE_NAME, OWNER, DATE_INSERTED);
COMMENT ON TABLE RDS$TOMENKE$GROWTH2 IS 'by Menke@RDS: Monitoring des Tab. Wachstums';
COMMIT;
create or replace PROCEDURE prc_mon_table_growth
AS
BEGIN
/*
By Torben Menke, RDS
*/
DELETE FROM RDS$TOMENKE$GROWTH1 t WHERE t.date_inserted < SYSDATE - 2*365; -- älter als 2 Jahre
DELETE from RDS$TOMENKE$GROWTH2 t WHERE t.date_inserted < SYSDATE - 2*365; -- älter als 2 Jahre
--1
INSERT INTO RDS$TOMENKE$GROWTH1
SELECT
s.SEGMENT_NAME AS "SEGMENT_NAME"
, s.OWNER
, (SELECT SYSDATE FROM dual) AS "date_inserted"
, SUM(s.BYTES) AS "BYTES"
, NULL
FROM DBA_SEGMENTS s
WHERE (s.TABLESPACE_NAME LIKE 'EC#_%' ESCAPE '#' OR s.TABLESPACE_NAME IS NULL) -- null für partitionierte
GROUP BY (s.SEGMENT_NAME, s.OWNER)
HAVING SUM(s.BYTES)>1*1024*1024 --1 MB Grenze
ORDER BY 1 ASC
;
--2
INSERT INTO RDS$TOMENKE$GROWTH2
SELECT
t.TABLE_NAME
, t.OWNER
, (SELECT SYSDATE FROM dual) AS "date_inserted"
, t.last_analyzed
, t.NUM_ROWS
, t.NUM_ROWS * t.AVG_ROW_LEN AS "BYTESNETTO"
, t.blocks * 8 * 1024 AS "BYTESBRUTTO"
, s.BYTES AS "BYTESSEGMENTS"
, NULL
FROM DBA_TABLES t -- all Tables ist nutzerabhängig und zeigt weniger Daten!
JOIN (
SELECT SEGMENT_NAME "TABLE_NAME", OWNER, SUM(BYTES) AS "BYTES"
FROM DBA_SEGMENTS s
WHERE segment_type LIKE 'TABLE%'
AND (s.TABLESPACE_NAME LIKE 'EC#_%' ESCAPE '#' OR s.TABLESPACE_NAME IS NULL) -- null für partitionierte
GROUP BY (SEGMENT_NAME, OWNER)
) s ON (t.TABLE_NAME = s."TABLE_NAME" and t.OWNER = s.OWNER)
WHERE (t.TABLESPACE_NAME LIKE 'EC#_%' ESCAPE '#' OR t.TABLESPACE_NAME IS NULL) -- null für partitionierte
AND t.NUM_ROWS * t.AVG_ROW_LEN >1*1024*1024 --1 MB Grenze
AND t.last_analyzed > nvl((SELECT MAX (m.date_inserted) FROM RDS$TOMENKE$GROWTH2 m ),sysdate-365) -- nur neue
--AND t.date_analyzed >= SYSDATE -7
ORDER BY 1 ASC
;
COMMIT;
END;
/
-- als ec-mon
grant execute on ec_mon.prc_mon_table_growth to ec_sys;
--Test
BEGIN
prc_mon_table_growth;
END;
/
-- Scheduler -> Kundenjob
--'BEGIN ec_mon.prc_mon_table_growth; end;'
/*
DELETE FROM RDS$TOMENKE$GROWTH1 t WHERE t.date_inserted > SYSDATE - 1 ;
DELETE from RDS$TOMENKE$GROWTH2 t WHERE t.date_inserted > SYSDATE - 1 ;
commit;
*/
SELECT t.DATE_INSERTED, COUNT(*) FROM RDS$TOMENKE$GROWTH1 t
GROUP BY t.DATE_INSERTED ORDER BY 1 DESC;
SELECT t.DATE_INSERTED, COUNT(*) FROM RDS$TOMENKE$GROWTH2 t
GROUP BY t.DATE_INSERTED ORDER BY 1 DESC;
-- Auswertung
-- 0
select t.date_inserted, count(*) from ec_mon.RDS$TOMENKE$GROWTH1 t
group by t.date_inserted
order by 1 desc ;
select t.date_inserted, count(*) from ec_mon.RDS$TOMENKE$GROWTH2 t
group by t.date_inserted
order by 1 desc ;
-- 1
SELECT t.segment_name, t.owner
,ROUND(last.bytes/1024/1024,1)
AS "MB last"
,ROUND(MAX (t.bytes)/1024/1024,1)
AS "max"
--,ROUND(AVG (t.bytes)/1024/1024,1)
-- AS "avg"
,ROUND(MIN (t.bytes)/1024/1024,1)
AS "min"
,ROUND(REGR_SLOPE(t.bytes , t.date_inserted-SYSDATE)/1024/1024*365,1)
AS "MB/Year"
,ROUND(100*REGR_SLOPE(t.bytes , t.date_inserted-SYSDATE)/AVG(t.bytes)*365,1)
AS "Pct/Year"
, round(max(t.date_inserted)-MIN(t.date_inserted),0) "days"
, COUNT(*) AS "count"
FROM ec_mon.RDS$TOMENKE$GROWTH1 t
JOIN
( --Sub 1, die zu jedem table_name das Maxdate_inserted sucht, sollte durch max(t.date_inserted) ersetzt werden können
SELECT
segment_name, owner , MAX(date_inserted) AS MaxDate
FROM ec_mon.RDS$TOMENKE$GROWTH1
GROUP BY segment_name, owner
) sub1
ON (t.segment_name = sub1.segment_name and t.owner = sub1.owner)
-- end sub1
JOIN
( --Sub 2, die jedem table_name und MaxDate die entsprechende Zeile liefert
SELECT *
FROM ec_mon.RDS$TOMENKE$GROWTH1
) "LAST"
ON (
t.segment_name = last.segment_name
AND t.owner = last.owner
AND last.date_inserted = sub1.MaxDate
)
-- end sub2
GROUP BY
t.segment_name, t.owner
,ROUND(last.bytes/1024/1024,1)
HAVING (
COUNT (*) > 1
AND AVG(t.bytes) != 0
-- AND MIN (t.bytes)!= MAX (t.bytes) -- nur Segmente mit Änderungen
)
ORDER BY 2 DESC
;
-- 2
/*
TODO: Wachstum an NumRows seit letztem Eintrag in absoluten Werten/Tag
*/
SELECT t.table_name, t.owner
,ROUND(last.num_rows/1E3,1)
AS "TRows last"
,ROUND(MAX(t.num_rows)/1E3,1)
AS "max"
--,ROUND(REGR_AVGY (t.num_rows , t.date_analyzed-SYSDATE)/1E3,1)
--,ROUND(AVG(t.num_rows)/1E3,1)
-- AS "avg"
,ROUND(MIN(t.num_rows)/1E3,1)
AS "min"
,ROUND(REGR_SLOPE(t.num_rows , t.date_analyzed-SYSDATE)/1E3*365,1)
AS "TRows/Year"
,ROUND(100*REGR_SLOPE(t.num_rows , t.date_analyzed-SYSDATE)/AVG(t.num_rows)*365,1)
AS "Pct/Year"
,ROUND(last.bytesnetto/1024/1024,1)
AS "MB netto last"
,ROUND(MAX(t.bytesnetto)/1024/1024,1)
AS "max"
--,ROUND(AVG(t.bytesnetto)/1024/1024,1)
-- AS "avg"
,ROUND(MIN(t.bytesnetto)/1024/1024,1)
AS "min"
,ROUND(REGR_SLOPE(t.bytesnetto , t.date_analyzed-SYSDATE)/1024/1024*365,1)
AS "MB/Year"
,ROUND(100*REGR_SLOPE(t.bytesnetto , t.date_analyzed-SYSDATE)/AVG(t.bytesnetto)*365,1)
AS "Pct/Year"
,ROUND(last.bytesbrutto/1024/1024,1)
AS "MB brutto last"
,ROUND(MAX(t.bytesbrutto)/1024/1024,1)
AS "max"
--,ROUND(AVG(t.bytesbrutto)/1024/1024,1)
-- AS "avg"
,ROUND(MIN(t.bytesbrutto)/1024/1024,1)
AS "min"
,ROUND(REGR_SLOPE(t.bytesbrutto , t.date_analyzed-SYSDATE)/1024/1024*365,1)
AS "MB/Year"
,ROUND(100*REGR_SLOPE(t.bytesbrutto , t.date_analyzed-SYSDATE)/AVG(t.bytesbrutto)*365,1)
AS "Pct/Year"
,ROUND((last.bytesbrutto - last.bytesnetto)/1024/1024,1)
AS "MB wasted"
,ROUND((last.bytesbrutto - last.bytesnetto)/last.bytesnetto,1)
AS "% wasted"
, round(max(t.date_analyzed) - MIN(t.date_analyzed),0) "days"
, max(t.date_analyzed) AS "date_analyzed"
--, sub3.LastDate14
, ROUND(100*(last.num_rows-last14.num_rows)/last14.num_rows,1) AS "2week%"
, COUNT(*) AS "count"
FROM ec_mon.RDS$TOMENKE$GROWTH2 t
left outer JOIN
( --Sub 1, die zu jedem table_name das MaxDate sucht
SELECT
table_name , MAX(date_analyzed) AS MaxDate
FROM ec_mon.RDS$TOMENKE$GROWTH2
GROUP BY table_name
) sub1
ON t.table_name = sub1.table_name
-- end sub1
left outer JOIN
( --Sub 2, die jedem table_name und MaxDate die entsprechende Zeile liefert
SELECT *
FROM ec_mon.RDS$TOMENKE$GROWTH2
) "LAST"
ON (
t.table_name = last.table_name
AND last.date_analyzed = sub1.MaxDate
)
-- end sub2
left outer JOIN ( --Sub 3, die zu jedem table_name das max(datum) von >14 Tagen vor maxdate sucht = LastDate14
SELECT table_name , MAX(date_analyzed) AS LastDate14 FROM ec_mon.RDS$TOMENKE$GROWTH2 t21
WHERE date_analyzed + 14 < (SELECT MAX(date_analyzed) FROM ec_mon.RDS$TOMENKE$GROWTH2 WHERE table_name = t21.table_name)
GROUP BY table_name
) sub3
ON t.table_name = sub3.table_name
-- end sub3
left outer JOIN
( --Sub 4, die jedem table_name und LastDate14 die entsprechende Zeile liefert
SELECT *
FROM ec_mon.RDS$TOMENKE$GROWTH2
) "LAST14"
ON (
t.table_name = last14.table_name
AND last14.date_analyzed = sub3.LastDate14
)
-- end sub4
WHERE last.bytesnetto != 0
AND t.ignore IS NULL
AND last.bytesnetto != 0
AND last14.num_rows != 0
GROUP BY
t.table_name, t.owner
,ROUND(last.num_rows/1E3,1)
,ROUND(last.bytesnetto/1024/1024,1)
,ROUND(last.bytesbrutto/1024/1024,1)
,ROUND((last.bytesbrutto - last.bytesnetto)/1024/1024,1)
,ROUND((last.bytesbrutto - last.bytesnetto)/last.bytesnetto,1)
--,ROUND(last14.num_rows/1E3,1)
--,sub3.LastDate14
, ROUND(100*(last.num_rows-last14.num_rows)/last14.num_rows,1)
HAVING (
COUNT (*) > 1
AND AVG(t.num_rows) != 0
AND AVG(t.bytesnetto) != 0
)
ORDER BY 6 desc --11 DESC
;
-- Auswertung in Excel-Form
-- Auswertung via Pivot (etwas falsch, da date_inserted statt date_analyzed
WITH pivot_data AS (
SELECT trunc(t.date_inserted) AS "Rows"
, t.table_name, ROUND(t.num_rows/1E3,0) AS "TRows"
FROM ec_mon.RDS$TOMENKE$GROWTH2 t
WHERE t.ignore IS NULL
)
SELECT * FROM pivot_data
PIVOT (
AVG("TRows")
FOR table_name
IN (
'DATENPOOL_ZEITREIHEN_LOG'
,'LINEARE_PROTOKOLLE_EXT'
,'TAGEBUECHER'
,'PULL_SYNC_READ'
,'EXPORT_LG'
,'FARZ_AUFTRAGSTRANSAKTIONEN'
,'FARZ_FNF_DETAILS'
,'ZR_DETAILS_HISTORIE'
)
)
ORDER BY 1 DESC
;
-- in MB
-- Auswertung via Pivot (etwas falsch, da date_inserted statt date_analyzed
WITH pivot_data AS (
SELECT trunc(t.date_inserted) AS "MByte"
, t.table_name, ROUND(t.bytesnetto/1024/1024,0) AS "MB"
FROM ec_mon.RDS$TOMENKE$GROWTH2 t
WHERE t.ignore IS NULL
)
SELECT * FROM pivot_data
PIVOT (
AVG("MB")
FOR table_name
IN (
'LINEARE_PROTOKOLLE_EXT'
,'EXPORT_LG'
,'IMP_EXP_TMP_DATA'
,'FARZ_AUFTRAGSTRANSAKTIONEN'
,'FARZ_FNF_DETAILS'
,'FARZ_MELDUNGEN'
,'ZR_DETAILS_VBOL_WERTE'
,'ZR_DETAILS_HISTORIE'
,'DATENPOOL_ZEITREIHEN_LOG'
,'TAGEBUECHER'
,'PULL_SYNC_READ'
)
)
ORDER BY 1 DESC
;
select * from ec_mon.RDS$TOMENKE$GROWTH2 t
where t.table_name like 'BER_LIS%'
;
/*
delete from ec_mon.RDS$TOMENKE$GROWTH2 t
where t.date_inserted > sysdate -1
;
delete from ec_mon.RDS$TOMENKE$GROWTH1 t
where t.date_inserted > sysdate -1
;
commit;
update ec_mon.RDS$TOMENKE$GROWTH2 t
set t.ignore = NULL
where 1=2;
*/
Tablespace Shrinking online via row movement
-- Table Shrinking Online - Free unused Space ALTER TABLE zr_details_historie ENABLE ROW MOVEMENT; ALTER TABLE zr_details_historie SHRINK SPACE CASCADE; -- 1200 sec / 20 min for 1 Mill Rows, 0.2 GB ALTER TABLE zr_details_historie disable ROW MOVEMENT;
Tablespace Shrinking offline via copying
by moving all tables to new tablespace
-- Analize wasted size
WITH wasted AS
(SELECT TABLESPACE_NAME, round(SUM(BYTES) / 1024 / 1024, 0) "MB wasted"
FROM DBA_FREE_SPACE
GROUP BY TABLESPACE_NAME)
SELECT df.tablespace_name,
df.file_name,
df.file_id,
round(df.bytes / 1024 / 1024, 0) AS "DF MB",
w."MB wasted"
FROM dba_data_files df
JOIN wasted w
ON w.TABLESPACE_NAME = df.TABLESPACE_NAME
ORDER BY 5 DESC;
-- try to reduce size, usually doesn't work
ALTER DATABASE datafile 27 resize 800M;
-- Tablespaces mit viel freiem Platz im Datafile suchen
SELECT TABLESPACE_NAME, round(SUM(BYTES) / 1024 / 1024 / 1024, 2) "GB wasted"
FROM DBA_FREE_SPACE
GROUP BY TABLESPACE_NAME
ORDER BY 2 DESC;
--EC_TBS_LOG hat viel Luft
select * from dba_tablespaces where tablespace_name='EC_TBS_LOG';
select * from dba_data_files where tablespace_name='EC_TBS_LOG';
-- /app/oradata/emtectn/ec_tbs_log01.dbf
-- Test Online-Verkleinerung
alter database datafile '/app/oradata/emtectn/ec_tbs_log01.dbf' resize 1500M;
-- geht nicht weil belegt am Ende ->
-- DB Jobs deaktivieren
select value from v$parameter where name='job_queue_processes';
--10
alter system set job_queue_processes=0;
-- Tabellen und Indizes auf Tablespace suchen
select * from dba_tables where tablespace_name='EC_TBS_LOG';
--EC_PRT.LINEARE_PROTOKOLLE
--EC_PRT.LINEARE_PROTOKOLLE_EXT
select * from DBA_INDEXES where tablespace_name='EC_TBS_LOG';
-- neuen Tabelspace anlegen
create tablespace test datafile '/app/oradata/emtectn/test.dbf' size 100M autoextend on next 10M;
alter user EC_PRT quota unlimited on test;
-- Tabellen verschieben
alter table EC_PRT.LINEARE_PROTOKOLLE move tablespace test;
alter table EC_PRT.LINEARE_PROTOKOLLE_EXT move tablespace test;
-- Tablespace verkleinern
alter database datafile '/app/oradata/emtectn/ec_tbs_log01.dbf' resize 1000M;
alter database datafile '/app/oradata/emtectn/ec_tbs_log01.dbf' resize 100M;
alter database datafile '/app/oradata/emtectn/ec_tbs_log01.dbf' resize 10M;
-- Tabellen zurück verschieben
alter table EC_PRT.LINEARE_PROTOKOLLE move tablespace EC_TBS_LOG;
alter table EC_PRT.LINEARE_PROTOKOLLE_EXT move tablespace EC_TBS_LOG;
-- Temp Tablespace löschen
drop tablespace test including contents and datafiles;
-- Indexe wieder reparieren, da durch verschieben UNSUEABLE geworden:
select * from dba_indexes where status not in ('VALID','N/A');
--reparieren via
alter index EC_PRT.LPL_SCHEDJOBS_AUDSID_I rebuild tablespace EC_TBS_PRTIDX;
alter index EC_PRT.LPL_ERRORLEVEL_I rebuild tablespace EC_TBS_PRTIDX;
alter index EC_PRT.LPL_PK rebuild tablespace EC_TBS_PRTIDX;
alter index EC_PRT.LPL_DATUM_I rebuild tablespace EC_TBS_PRTIDX;
alter index EC_PRT.LPL_BESCHR_I rebuild tablespace EC_TBS_PRTIDX;
alter index EC_PRT.LPE_LPL_LFDNR_I rebuild tablespace EC_TBS_PRTIDX;
-- Jobs wieder starten
alter system set job_queue_processes=10;
Index Rebuild
--Index neu erstellen / rebuild ALTER INDEX EC_ZRV.ZRDH_PK REBUILD; ALTER INDEX EC_ZRV.ZRDH_PK REBUILD ONLINE; -- bei Tabellen mit wenig Bewegung
User
-- Infos zu DB Users
select username, account_status,created, profile from dba_users order by username
-- generate alter user commands for password change of many users
SELECT 'alter user ' || username || ' identified by PASSWORD;' || ' --Status = ' || ACCOUNT_STATUS -- 1 liner
FROM dba_users
WHERE upper(username) LIKE '%menke%' ESCAPE '#'
OR oracle_maintained = 'Y' -- Oracle User
ORDER BY username;
-- Nur Oracle User und schauen ob aktiv
SELECT 'alter user ' || username || ' identified by PASSWORD;' || ' --Status = ' || ACCOUNT_STATUS
FROM dba_users
WHERE 1=1
and oracle_maintained = 'Y' -- Oracle User
and ACCOUNT_STATUS not like 'EXPIRED _ LOCKED'
ORDER BY username;
-- Oracle User with Std / Default Password
select * from sys.dba_users_with_defpwd order by username;
-- Passwort PW aller User ändern
-- Befehle via Select generieren
select 'alter user '||username||' identified by <new_pwd>;' from dba_users order by username;
-- Liste aller USER
select * from dba_users where ORACLE_MAINTAINED = 'N' by 1;
-- User anlegen
CREATE USER tmenke IDENTIFIED BY passwort;
GRANT CREATE SESSION TO tmenke;
-- Login Sperren
ALTER USER tmenke ACCOUNT LOCK;
-- Login EntSperren
ALTER USER tmenke ACCOUNT UNLOCK;
-- Passwort ändern und auf abgelaufen (=neu zu vergeben) setzen
alter user tmenke identified by "Start123" password expire
-- Statements generieren um alle non-oracle User zu sperren
SELECT 'ALTER USER ' || username || ' ACCOUNT LOCK ;' FROM dba_users WHERE ORACLE_MAINTAINED = 'N' order by username;
-- User löschen
DROP USER tmenke cascade ;
-- Statements generieren um alle non-oracle User zu löschen (vor Datapump Import von Backup)
SELECT 'DROP USER ' || username || ' cascade ;' FROM dba_users WHERE ORACLE_MAINTAINED = 'N' order by username;
-- Rollen eines Users anzeigen
-- your permissions
select * from USER_ROLE_PRIVS where USERNAME = USER;
select * from USER_TAB_PRIVS where Grantee = USER;
select * from USER_SYS_PRIVS where USERNAME = USER;
-- granted role permissions
select * from ROLE_ROLE_PRIVS where ROLE IN (select granted_role from USER_ROLE_PRIVS where USERNAME = USER);
select * from ROLE_TAB_PRIVS where ROLE IN (select granted_role from USER_ROLE_PRIVS where USERNAME = USER);
select * from ROLE_SYS_PRIVS where ROLE IN (select granted_role from USER_ROLE_PRIVS where USERNAME = USER);
-- check for locked user accounts
SELECT username, account_status FROM dba_users ORDER BY username;
-- Lock DB User
ALTER USER ec_admin ACCOUNT LOCK;
-- Unlock DB User
ALTER USER ec_admin ACCOUNT UNLOCK;
--- Passwortänderung und Unlock
alter user menke identified by pwdofmenke ACCOUNT Unlock ;
-- Drop User
DROP USER menke CASCADE
Sessions
-- laufende DB Jobs anzeigen
SELECT * FROM DBA_JOBS_RUNNING;
-- job_queue_processes einstellen
select value from v$parameter where name='job_queue_processes';
alter system set job_queue_processes = 8 scope = both;
-- Empfehlung
optimistischer: Anzahl CPU*2 - 2 (= wegen Hyperthreadling - Puffer für Masken etc)
Anzahl CPU, zB via select * from v$parameter where name like '%cpu_count%';
-- DBMS Scheduler Jobs - DBA
SELECT * FROM dba_scheduler_running_jobs;
-- DBMS Scheduler Jobs - User
SELECT * FROM user_scheduler_running_jobs;
--starten
BEGIN
job_name => 'DELETE_WF_LOG',
dbms_scheduler.create_job (
job_type => 'plsql_block',
job_action => 'begin ec_wfb.prc_del_wf_log; end;',
ENABLED => TRUE);
END;
--stoppen
exec DBMS_SCHEDULER.stop_JOB (job_name => '<NAME>');
-- oder
BEGIN
dbms_scheduler.drop_job(job_name => 'DELETE_WF_LOG', FORCE => TRUE);
END;
-- Langläufer
-- aktuell laufende + Fortschritt (%) + ETA
SELECT t.opname , t.target , t.username AS "user"
, ROUND(t.sofar/t.totalwork,2)*100 AS "pct_done"
, ROUND(t.ELAPSED_SECONDS / ( t.sofar/t.totalwork ) /60 ,0) AS "Min, tot"
, ROUND( ( t.START_TIME + (t.ELAPSED_SECONDS / ( t.sofar/t.totalwork ) /24/3600 ) - SYSDATE ) *24*60,0) AS "Min to go"
, t.START_TIME + (t.ELAPSED_SECONDS / ( t.sofar/t.totalwork ) /24/3600 ) AS "eta"
, t.START_TIME AS "start"
FROM v$session_longops t WHERE t.sofar!=t.totalwork AND t.totalwork!=0 AND t.sofar != 0
ORDER BY 5 DESC
;
--get sid and serial for all sessions of a user
SELECT sid, serial# FROM v$session WHERE username = 'EC_TSI';
--get sid and serial for all sessions of an non-oracle users
SELECT username, sid, serial# FROM v$session WHERE username in (select username FROM dba_users WHERE ORACLE_MAINTAINED = 'N');
--Kill SESSION
alter system kill session '439,22749';
-- oder brutaler:
alter system kill session '439,22749' immediate;
-- get kill commands for all session of a user
SELECT 'alter system kill session || sid || ',' || serial# || ;' FROM v$session WHERE username = 'ec_admin';
-- get kill commands for all session of all non-oracle users
SELECT 'alter system kill session || sid || ',' || serial# || ; -- ' || username
FROM v$session
WHERE 1 = 1
AND username IN (SELECT username FROM dba_users WHERE ORACLE_MAINTAINED = 'N')
ORDER BY username;
-- session, CPU_TIME und SQL dazu gejoint zum finden langer Sessions
SELECT sesion.sid,
sesion.serial#,
sesion.username,
optimizer_mode,
hash_value,
address,
cpu_time,
elapsed_time,
sql_text
FROM v$sqlarea sqlarea, v$session sesion
WHERE sesion.sql_hash_value = sqlarea.hash_value
AND sesion.sql_address = sqlarea.address
AND sesion.username IS NOT NULL
ORDER BY cpu_time DESC;
-- letzte 24 h
SELECT opname,
target,
COUNT(*) "COUNT",
SUM(t.ELAPSED_SECONDS) "SUM",
ROUND(AVG(t.ELAPSED_SECONDS), 1) "AVG"
FROM v$session_longops t
GROUP BY opname, target
ORDER BY 3 DESC;
--Sessions von Benutzer auflisten
SELECT osuser, COUNT(*) FROM v$session t GROUP BY osuser ORDER BY 1;
SELECT * FROM v$session t WHERE osuser = 'albrosel';
AWR Snapshot
-- AWR SnapShot Intervall
EXEC dbms_workload_repository.modify_snapshot_settings(interval=>10);
SELECT extract(DAY FROM snap_interval) * 24 * 60
+ extract(hour FROM snap_interval) * 60
+ extract(minute FROM snap_interval) AS "Snapshot Interval"
, extract(DAY FROM retention) * 24 * 60
+ extract(hour FROM retention) * 60
+ extract(minute FROM retention) AS "Retention Interval"
FROM dba_hist_wr_control;
-- AWR Snapshot starten:
EXECUTE DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT();
DB_RECOVERY_FILE
-- Set DB_RECOVERY_FILE ALTER SYSTEM SET DB_RECOVERY_FILE_DEST_SIZE=20G SCOPE=BOTH; ALTER SYSTEM SET DB_RECOVERY_FILE_DEST='E:\ORACLE\ORADATA\CEMTEC\arch' SCOPE=BOTH;
Tracing
-- Trace von Session starten
DBMS_MONITOR.SESSION_TRACE_ENABLE(
session_id
,serial_num
,TRUE
,TRUE
)
-- Tracing an entire database
--Start:
ALTER SYSTEM SET sql_trace = true SCOPE=MEMORY;
--Stop:
ALTER SYSTEM SET sql_trace = false SCOPE=MEMORY;
-- Tracing an entire database
--Start:
ALTER SYSTEM SET sql_trace = true SCOPE=spfile;
ALTER SYSTEM SET max_dump_file_size = UNLIMITED SCOPE=spfile;
startup force;
--Stop:
ALTER SYSTEM SET sql_trace = false SCOPE=spfile;
ALTER SYSTEM SET max_dump_file_size = <alter Wert> SCOPE=spfile;
startup force;
-- Session finden zum Tracen
SELECT s.sid,
s.serial#,
s.CLIENT_IDENTIFIER,
s.SERVICE_NAME,
s.MODULE,
s.ACTION,
s.username,
s.LOGON_TIME,
s.STATUS,
s.osuser,
s.SERVER,
s.PROCESS,
s.MACHINE,
s.TERMINAL,
s.PROGRAM,
s.TYPE,
s.CLIENT_INFO,
p.TERMINAL AS p_terminal,
p.PROGRAM AS p_program
FROM v$session s, v$process p
WHERE s.paddr = p.addr
AND s.TYPE NOT IN ('BACKGROUND')
AND status = 'ACTIVE'
ORDER BY;
-- service_name='SYS$USERS' ; Module='pkg_ewe_kabs_endur' und/oder Module='eCOUNTprognose.exe'
Apex
-- APEX Versionsnummer --1 select * from apex_release; --2 -- Select current APEX user over all_users/dba_users select max(u1.username) as current_apex_user from all_users u1 where u1.username like 'APEX%' and REGEXP_LIKE (substr(u1.username,'6',1), '^[0-9]*$');
Hacks
-- Zugriff auf Stand von Vergangenheit via Undo Segmente
-- benötigt Grant execute on DBMS_FLASHBACK to username;
SELECT * FROM email_abos AS OF TIMESTAMP to_timestamp('08-01-2015 07:15', 'dd-mm-yyyy hh24:mi:ss');
Restore Point
= einfacher und schneller als RMAN Backup einspielen. bei Creation (in Open Mode): create restore point DB_23012018_1120 guarantee flashback database; Test: select * from v$restore_point; Flashback to the guaranteed restore point: SQL> shutdown immediate; SQL> startup mount; SQL> flashback database to restore point DB_23012018_1120; SQL> alter database open resetlogs;
Datapump Export Import
CREATE OR REPLACE DIRECTORY "DATA_PUMP_DIR" AS '/app/oracle/backup/datapump-EMTECTN'; select * from dba_directories; GRANT READ, WRITE ON DIRECTORY DATA_PUMP_DIR TO SYSTEM;
Export
-- stop DB jobs select value from v$parameter where name='job_queue_processes'; --10 alter system set job_queue_processes = 0 scope = both; -- Update Stats for better performance exec dbms_stats.gather_dictionary_stats; exec dbms_stats.lock_table_stats (null, 'X$KCCLH'); exec dbms_stats.gather_fixed_objects_stats; exec dbms_stats.gather_system_stats; -- Export via SQPlus . oraenv mydatabase expdp system/xxx full=Y parallel=6 consistent=Y directory=DATA_PUMP_DIR dumpfile=DumpV1_%u.dmp logfile=DumpV1.log -- alternative via parameterfile expdp system/emtectn @string parfile=parfile.par -- restart jobs alter system set job_queue_processes = 10 scope = both;
Import
-- cleanup target DB -- 1. lock all nun-oracle users SELECT 'ALTER USER ' || username || ' ACCOUNT LOCK ;' FROM dba_users WHERE ORACLE_MAINTAINED = 'N' order by username; -- 2. Kill Sessions of all non-oracle users SELECT 'alter system kill session || sid || ',' || serial# || immediate ; -- ' || username FROM v$session WHERE 1 = 1 AND username IN (SELECT username FROM dba_users WHERE ORACLE_MAINTAINED = 'N') ORDER BY username; -- 3. Drop non-oracle users. (preferable in several parallel sessions) SELECT 'DROP USER ' || username || ' cascade ;' FROM dba_users WHERE ORACLE_MAINTAINED = 'N' order by 1; purge recyclebin; alter session set optimizer_mode=choose; -- Update Stats for better performance exec dbms_stats.gather_dictionary_stats; exec dbms_stats.lock_table_stats (null, 'X$KCCLH'); exec dbms_stats.gather_fixed_objects_stats; exec dbms_stats.gather_system_stats; -- Check if DB is empty . oraenv mydatabase sqlplus / as sysdba select username from dba_users where ORACLE_MAINTAINED='N'; -- must be empty! --Import exclude=FUNCTIONAL_INDEX impdp directory=EXPIMP parallel=6 dumpfile=Dump_17-12-14_15-03-%u.dmp logfile=imp_17-12-14_15-03_EFI.log exclude=FUNCTIONAL_INDEX username: / as sysdba -- Sysgrants sqlplus / as sysdba -- @/app/dump/emtectn/sys_grants-emtecp-171127.sql -- Import include=FUNCTIONAL_INDEX impdp directory=EXPIMP parallel=6 dumpfile=Dump_17-12-14_15-03-%u.dmp logfile=imp_17-12-14_15-03_FI.log include=FUNCTIONAL_INDEX username: / as sysdba -- Recompile aller invaliden Objecte: sqlplus / as sysdba @/app/oracle/product/12.1.0.2/ee/rdbms/admin/utlrp.sql
SQL
INSERT
INSERT INTO departments (department_id, department_name) VALUES (30, 'purchasing');
--oder
INSERT INTO departments VALUES (100, 'finance', NULL, NULL);
INSERT INTO sales_reps
(id, NAME, salary, commission_pct)
SELECT employee_id, last_name, salary, commission_pct FROM employees WHERE job_id LIKE '%rep%';
INSERT INTO CERT_KEY_KEYS
(NAME, DESCRIPTION, RCKA_ID, CKE_ID)
VALUES
('JKS-Key', , -2, (SELECT id FROM CERT_KEY_ENTITIES WHERE NAME = 'JKS Entity'));
-- viele Inserts in einer Transaktion
INSERT all
INTO rds$tmenke$ecounttabellen (owner, table_name) VALUES ('EC_ANF', 'ANFRAGEN')
INTO rds$tmenke$ecounttabellen (owner, table_name) VALUES ('EC_ANF', 'ANFRAGE_PARAMETER')
INTO rds$tmenke$ecounttabellen (owner, table_name) VALUES ('EC_ANF', 'ANFRAGEVERMERKE')
INTO rds$tmenke$ecounttabellen (owner, table_name) VALUES ('EC_ANF', 'KUNDEN')
SELECT 1 FROM DUAL; -- select muss hier sein
-- Insert mit Select kombinieren
INSERT INTO CERT_KEY_KEYS
(NAME, DESCRIPTION, RCKA_ID, CKE_ID)
VALUES
('JKS-Key', , -2, (SELECT id FROM CERT_KEY_ENTITIES WHERE NAME = 'JKS Entity'));
CREATE TABLE
-- Copy Table / Kopie von Tabelle anlegen
CREATE table zvw_zaehler_bak as select * from zvw_zaehler;
COMMENT ON table zvw_zaehler_bak IS 'Kopie zvw_zaehler vom 26.04.2016 by Torben Menke';
-- structure only, no data
create table employees2
as select *
from employees
where 1=2
;
Constraints
--Add Constraint on TABLESPACE_NAME and LAST_ANALYZED ALTER TABLE RDS$ToMenke$Monitoring2 ADD CONSTRAINT RDS$ToMenke$Mon2$name_date UNIQUE (TABLE_NAME, LAST_ANALYZED);
Grants
GRANT SELECT ON myTable TO otheruser; -- Change Table Tabelle Owner by copying --user1 GRANT ALL PRIVILEGES ON myTable TO user2; COMMIT; --user2 CREATE TABLE myTable AS SELECT * FROM user1.myTable COMMIT; --user1 DROP TABLE user1.myTable; COMMIT;
UPDATE
UPDATE employees
SET (salary) = (10000)
WHERE employee_id = 113;
UPDATE employees -- for all
SET (salary) = (10000);
UPDATE employees
SET (salary) =
(SELECT salary FROM employees WHERE employee_id = 205)
WHERE employee_id = 113;
--Replace
--Replace: wirkt nur auf die Spalten einer Tabelle, die einen Match haben
update EMAIL_ABOS set ABO_BEZEICHNUNG = replace(ABO_BEZEICHNUNG,'24T1','24-T1') ;
update EMAIL_ABOS set Anhangdateiname = replace(Anhangdateiname,'24T1','24-T1') ;
-- Zeilen schützen auch bei Select möglich
select employee_id, salary, commission_pct, job_id
from employees
where job_id = 'SA_REP'
for update -- hier wird sperre eingefügt
order by employee_id;
-- Update mit Select kombinieren
UPDATE employees
SET (salary) =
(SELECT salary FROM employees WHERE employee_id = 205)
WHERE employee_id = 113;
-- Update all
UPDATE employees -- for all
SET (salary) = (10000);
DELETE
DELETE FROM departments WHERE department_name = 'Finance'; DELETE FROM departments; -- delete all (schneller: TRUNCATE TABLE departments;) -- Datumsfunktionen ALTER SESSION SET NLS_DATE_FORMAT='DD.MM.YYYY HH24:MI:SS'; TO_CHAR (lp.datum,'YYYY-MM') -- unperformant TRUNC(t.von,'mon') -- -> 1.2.2015 -- Day Of Week (1 = Montag, 7 = Sonntag) 1 + TRUNC(t.von) - TRUNC(t.von, 'IW') "WT" -- List of Dates SELECT TRUNC (SYSDATE - ROWNUM) dt FROM DUAL CONNECT BY ROWNUM <= 31 -- delete all delete from copy_emp; -- or truncate (=faster, but no rollback possible) truncate table copy_emp; -- Delete mit Select kombinieren DELETE FROM tab2 WHERE tab2.id IN (SELECT id FROM tab1 WHERE id < 100);
NLS_Session_parameters / Date Format
-- Spracheinstellungen anzeigen select * from NLS_Session_parameters ; -- oder SQL Developer -> Tools -> Preferences -> Database -> NLS alter session set nls_language = 'german'; -- nur sprache alter session set nls_territory = 'germany'; -- alle localizations -- wieder std zurück alter session set nls_language = 'american'; -- nur sprache alter session set nls_territory = 'america'; -- alle localizations ALTER SESSION SET NLS_DATE_FORMAT='DD.MM.YYYY HH24:MI:SS';
String Zeichenketten Functions
-- String Zeichenketten Functions http://docs.oracle.com/cd/E17952_01/refman-5.1-en/string-functions.html -- Zeichenketten zusammenfügen -- spalte1 || spalte2 = concat (spalte1,spalte2) -- upper lower initcap -- Platzhalter in Zeichenketten nur in LIKE -- % Steht für eine beliebige Folge von keinem oder mehreren Zeichen -- _ Steht für ein beliebiges einzelnes Zeichen -- Platzhalter durch Escape (hier !) select employee_id, last_name, job_id from employees where job_id like '%SA!_%' escape '!' ;
Substitution Variables / Austauschvariablen
-- Eingabedialog -> Variable via &variable (nur SQL*Plus / SQL Developer) select employee_id, last_name, salary, department_id from employees where employee_id = &employee_num -- zB 143 ; select employee_id, last_name, salary, department_id from employees where last_name = '&last_name' --zB Matos ; -- && verwenden, wenn der Variablenwert wiederverwendet werden soll, ohne dass der Benutzer jedes mal zur Eingabe aufgefordert wird accept enumber number prompt 'Bitte Employee ID eingeben :' select first_name, last_name, salary from employees where employee_id=&enumber ;
synonym
-- synonym erspart tippen create synonym e for employees ; select last_name, first_name from e ; select * from user_synonyms ; drop synonym e;
DUAL
-- ist eine öffentliche Tabelle, in der Ergebnisse aus Funktionen und Berechnungen angezeigt werden.
select round(45.923,2), round(45.923,0), round(45.923,-1)
from dual
;
select 'asdfghj' as zeichenkette
from dual ;
select substr('asdfghj',1,3)
from dual ;
select trim ('a' from 'asdffdsa') as both
from dual ;
select replace('asdfghj','a','b')
from dual ;
Date Functions
--datum-zeugs
-- datum-datum = tage -> week via as weeks
select last_name, (sysdate-hire_date)/7 as weeks
from employees ;
select to_date ('12-jan-1981') + 10000
from dual ;
-- formate: http://docs.oracle.com/cd/E16655_01/server.121/e17209/sql_elements004.htm#SQLRF51079
--FM = no leading ' ' or 0
select to_char(to_date ('12-jan-1981') + 10000, 'DD.MM.YYYY')
from dual ;
select add_months ('29-jan-1981',1) -- Vorsicht: 31.1. +1mon = 29.2. :-(
from dual ;
select last_day ('29-jan-1981') -- letzer Tag des Monats
from dual ;
CASE
-- A select case
select last_name, job_id, salary,
case job_id -- nur bei select case
when 'IT_PROG' then 1.10*salary
when 'ST_CLERK' then 1.15*salary
when 'SA_REP' then 1.20*salary
else salary
end as "Revised_Salary"
from employees;
-- B search case
select last_name, job_id, salary,
case -- bei search case keine Angabe des Feldes hier
when salary < 5000 then 'Low'
when salary < 10000 then 'Med'
when salary < 15000 then 'High'
else 'Excellent'
end as "Gehaltseinteilung"
from employees;
-- Alternative : Decode (=Oracle-spezifisch)
-- nur select, kein search
-- besser case verwenden
select last_name, job_id, salary,
decode (
job_id
,'IT_PROG' , 1.10*salary
,'ST_CLERK', 1.15*salary
,'SA_REP' , 1.20*salary
,salary)
as revised_salary
from employees
;
select last_name, salary,
decode (trunc(salary/2000, 0), -- sal/2000 und komma abschneiden
0, 0.00,
1, 0.09, --falls trunc(sal/2000,0) == 1 -> gib 0.09 zurück
2, 0.20,
3, 0.30,
4, 0.40,
5, 0.42,
6, 0.44,
0.45) --else
as tax_rate
from employees
;
Gruppenfunktionen
/* avg count max min sum listagg stddev variance */
select count (salary) as gehalt from employees ; -- Anzahl Gehälter
select count (distinct salary) as gehalt from employees ; -- Anzahl verschiedene Gehälter
-- Die Funktion NVL zwingt Gruppenfunktionen, Nullwerte einzubeziehen: (hier durch null->0)
select avg(nvl(commission_pct, 0)) from employees ;
select department_id, round(avg(salary))
from employees
group by department_id -- muss nicht im select beinhaltet sein
order by 1
;
select department_id, job_id, sum(salary)
from employees
group by department_id, job_id -- doppelte Gruppierung (mehr als 2 ist nicht möglich)
order by job_id
;
-- Gruppe mit Einschränkung
select department_id, max(salary)
from employees
where job_id not like '%REP%' --Zeilenfilter kommt vor Gruppenfilter
group by department_id
having max(salary)>10000 -- Gruppenfilter
;
-- Gruppenfunktionen verschachteln
select max(avg(salary))
from employees
group by department_id
;
-- CountIF
select count (*) as "Total"
, sum( case when to_char(hire_date,'YYYY') = 2005 then 1 else 0 end ) as "2005"
from employees
;
-- SumIf
select
sum( case when department_id=20 then salary else NULL end ) as "Dep20"
from employees
group by job_id
order by job_id
;
--ListAgg -> Elemente zusammen pappen
select department_id, listagg (last_name, ',') within group (order by last_name) as empl
from employees
group by department_id
;
JOINs
/* Typen von Joins:
INNER JOIN gibt nur übereinstimmende Zeilen zurück
(self) JOIN Tab1.col1 mit Tab1.col2
Es gibt drei Typen von OUTER Joins
LEFT OUTER gibt auch nicht übereinstimmende der 1. Tabelle mit zurück (mit NULL-Wert)
RIGHT OUTER ... 2.
FULL OUTER
CROSS JOIN Kreuzprodukt / kartesisches Produkt
= Alle möglichen Kombinationen von Tab1 mit Tab2
-> Ergebnis ist sehr groß...
*/
select department_name, city
from departments
natural join locations -- location_id kommt in beiden Tabs vor
;
-- selbiges unter Verwendung von alias
select d.department_name, l.city
from departments d
natural join locations l
;
-- selbiges via "join using"
select department_name, city
from departments
join locations
using (location_id)
;
-- selbiges via "join on"
select d.department_name, l.city
from departments d
join locations l
on (d.location_id = l.location_id)
;
-- 3 Tabellen joinen
select e.employee_id, l.city, d.department_name
from employees e
join departments d
on d.department_id = e.department_id
join locations l
on d.location_id = l.location_id
;
-- joinen über 2 Schlüssel
select j.employee_id, j.start_date, j.job_id
from employees e
join job_history j
on (j.employee_id = e.employee_id and e.hire_date = j.start_date)
order by j.employee_id
;
-- Alte Oracle Syntax
select e.last_name, d.department_name
from employees e, departments d
where e.department_id = d.department_id --wenn hier (+) angegeben, dann outer join
;
--self-join
select e.last_name as "Malocher", b.last_name as "Boss"
from employees e
join employees b -- aliasnamen
on (e.manager_id = b.employee_id)
;
--non-equal join : Zahl -> Bereich -> Sclüssel
select e.last_name, e.salary, j.grade_level
from employees e
join job_grades j
on e.salary between j.lowest_sal and j.highest_sal
;
-- outer join
select e.last_name, e.department_id, d.department_name
from employees e
left outer join departments d
on (e.department_id = d.department_id)
;
--cross join = karthesisches Produkt
select e.last_name, d.department_name
from employees e
cross join departments d;
--alte Oracle Syntax:
select e.last_name , d.department_name
from employees e , departments d
-- where e.department_id = d.department_id
;
-- where als alternative zu join on:
-- gibt mir depid, name, city, region etc!
select d.department_id, d.department_name, l.city, c.country_name, r.region_name
from departments d
join locations l using (location_id)
join countries c using (country_id)
join regions r using (region_id)
order by d.department_id
SubQueries / Unterabfragen
select last_name, salary
from employees
where salary >
( -- single row unterabfrage
select salary
from employees
where last_name = 'Abel'
-- prob wenn 2 den Namen Abel haben: besser where salary > all (
-- performanter: select max (salary) in Unterabfrage
)
;
select last_name, salary, department_id
from employees
where salary in -- in liste
(
select min(salary)
from employees
group by department_id
) ;
select employee_id, last_name, job_id, salary
from employees
where job_id != 'IT_PROG'
and salary < any --Vergleich wird mit jedem Element der Untergruppe verglichen, muss für ein beliebiges Element eintreffen
(
select salary
from employees
where job_id = 'IT_PROG'
) ;
select employee_id, last_name, job_id, salary
from employees
where job_id != 'IT_PROG'
and salary < all --Vergleich wird mit jedem Element der Untergruppe verglichen, muss für alle eintreffen
(
select salary
from employees
where job_id = 'IT_PROG'
) ;
select employee_id, last_name, job_id, salary
from employees
where job_id != 'IT_PROG'
and salary <
(
select min (salary)
from employees
where job_id = 'IT_PROG'
) ;
--WHERE EXISTS -> TRUE falls Unterabfrage min einen Treffer hat
select employee_id,salary,last_name
from employees m
where exists
( select employee_id
from employees w
where (w.manager_id=m.employee_id) and w.salary > 10000
) ;
-- für jede Hauptabfrage wird die Unterabfrage einmal ausgführt
-- In _jeder_ Zeile der Tabelle employees wird die Bedingung geprüft, ob eine manager_id mit einem Gehalt über $10.000 existiert.
-- geht vielleicht auch kürzer
-- Problem mit "NOT IN": bei NULL Werten
--alle dep ohne mitarbeiter
select d.department_name
from departments d
where not exists (
select e.department_id
from employees e
where e.department_id = d.department_id
) ;
select d.department_name
from departments d
where d.department_id not in (
select e.department_id
from employees e
-- WICHITG!!! weil oberes NOT IN gibt false wenn null in Ergebnismenge
where e.department_id is not null
-- Alternative : NullWert fixen:
-- select NVL(e.department_id,0)
) ;
-- Alternative mit NOT EXISTS
select d.department_name
from departments d
where not exists (
select 'X'
from employees e
where d.department_id = e.department_id
) ;
-- schöne Alternative mit MengenOP (Lession 9)
select d.department_name
from departments d
where d.department_id in
(
select d.department_id from departments d
minus
select distinct e.department_id from employees e
)
;
Mengen Operatoren
-- =interessant für Unterabfragen -- UNION (keine doppelten) ACHTUNG: sortiert die Ergebnisse -- UNION ALL (auch doppelte) ACHTUNG: keine Sortierung -- INTERSECT (Schnittmenge) -- MINUS (TabA-TabB) -- aktuelle u. ehemalige Tätigkeiten aller Mitarbeiter select employee_id, job_id from employees union select employee_id, job_id from job_history ; --welcher MA haben wieder den gleichen Job select employee_id, job_id from employees intersect select employee_id, job_id from job_history ; -- Personalnummern der Mitarbeiter anzeigen, die noch nie die Tätigkeit gewechselt haben select employee_id from employees minus select employee_id from job_history ;
transaction controll
SAVEPOINT name -- oracle only COMMIT ( Transaktionen werden übernommen + entfernt auch savepoints ) ROLLBACK [to SAVEPOINT]
WITH
with
dept_costs as ( -- DepName | Sum(Salary)
select d.department_name , sum(e.salary) as dept_total
from employees e
join departments d
on e.department_id = d.department_id
group by d.department_name
)
,
avg_cost as ( -- eine Zahl, berechnet via dept_costs von oben
select sum(dept_total)/count(*) as dept_avg
from dept_costs
)
select *
from dept_costs
where dept_total > (select dept_avg from avg_cost)
order by department_name
;
PIVOT
(ab Oracle 11g)
WITH pivot_data AS ( SELECT deptno, job, salvali FROM emp ) SELECT * FROM pivot_data PIVOT ( SUM(sal) --<-- pivot_clause FOR deptno --<-- pivot_for_clause IN (10,20,30,40) --<-- pivot_in_clause ); --Alternative Syntax ohne WITH SELECT * FROM ( SELECT deptno, job, sal FROM emp ) PIVOT ( SUM(sal) --<-- pivot_clause FOR deptno --<-- pivot_for_clause IN (10,20,30,40) --<-- pivot_in_clause );
CUBE
select department_id, job_id, sum(salary), count(employee_id) from employees e where e.department_id < 60 -- einfach: -- group by (department_id, job_id) -- erweitert via cube group by cube (department_id, job_id) -- cube erzeugt /* 1. Gruppierung über nix (*,*) 2. Gruppierung über beides (A,B) , wie normales group by 3.+4. Gruppierung auf (A,*) und (*,B) dabei wird * -> NULL */ ;
Regular Expressions
select first_name, last_name from employees where regexp_like(first_name,'^ste(ph|v)en$','i') -- i = case-insensitive ; select last_name, phone_number as phone1 , regexp_replace(phone_number, '(\d+)\.(\d+)\.(\d+)', '\3-\2-\1') as phone2 from employees ;
Views
create view empvu80 as select employee_id, last_name, salary from employees where department_id = 80 ; select * from empvu80; select * from user_views; select text from user_views where view_name = 'EMPVU80'; drop view empvu80; -- Create or replace view -- or replace: behält beim ersetzen die Zugriffsrechte anderer Nutzer auf meine Tabelle! -- update view and rename columns create or replace view empvu80 (id_number, name, sal, department_id) as select employee_id, first_name || ' ' || last_name, salary, department_id from employees where department_id = 80 ; -- WITH CHECK OPTION CONSTRAINT = WICHTIG!!! -- stellt sicher, dass constraint eingehalten wird, hier depID=20 create or replace view empvu20 as select * from employees where department_id = 20 with check option constraint empvu20_ck ;