Mittwoch, Juni 30, 2010

Interne Repräsentation von Number-Werten

Auf Steve Adams ixora-Seite findet man eine kurze Erläuterung zur internen Repräsentation von NUMBER-Werten. Die interne Darstellung entspricht einer wissenschaftlichen Notation mit Exponent und Mantisse. Dabei enthält das erste Byte der Repräsentation ein Bit für das Vorzeichen und den Exponenten, dann folgt die Mantisse, wobei jedes Byte normalerweise zwei Dezimalstellen darstellt. Mit der Funktion DUMP kann man die interne Repräsentation einer Zahl darstellen lassen:

select dump(1.23) from dual;

DUMP(1.23)
---------------------
Typ=2 Len=3: 193,2,24

select dump(123) from dual;

DUMP(123)
---------------------
Typ=2 Len=3: 194,2,24

select dump(1230000) from dual;

DUMP(1230000)
---------------------
Typ=2 Len=3: 196,2,24

Der Exponent ändert sich (193 -> 194 -> 196), während die Mantisse identisch bleibt (2, 24). Bei Gelegenheit sollte ich mir die Details noch etwas genauer anschauen.

SQL_TRACE für Einzelstatements

noch ein Link - eigene Gedanken mache ich mir dieser Tage nicht ...
Diesmal auf Tanel Poders Blog, in dem er erwähnt, dass es in 11.2 möglich ist, SQL Trace für eine bestimmte SQL-Id zu aktivieren. Das sieht dann etwa folgendermaßen aus:

alter session set events 'sql_trace[SQL: 9hb0kku1ncc9b] 
              {callstack: fname opiexe} 
              plan_stat=all_executions,wait=true,bind=true';

Bei Tanel Poder erscheint noch die Option {pgadep: exactdepth 0}, die in meinem System (11.1.0.7.0) nicht funktioniert hat.

Nachtrag 17.11.2011: Maria Colgan beschreibt im Blog der cbo Entwickler das entsprechende Verfahren zur Erstellung eines Optimizer-Traces (Event 10053).

Freitag, Juni 25, 2010

Locks

Und noch eine Referenz auf Jonathan Lewis' Blog, wo er die Matrix der Lock-Typen in Oracle darstellt.

32bit und 64bit

In Jonathan Lewis' Blog findet sich ein sehr interessantes Fallbeispiel, in dem der Umstieg von 32bit auf 64bit zu Problemen mit der Speichernutzung führt.

Three kind of lies

"There are three kinds of lies: lies, damned lies, and statistics."

Benjamin Disraeli (behauptet jedenfalls Mark Twain)

Und was ist mit BI?

SQL Server DMVs

Allmählich komme ich dahinter, dass die Dynamic Management Views (DMVs) des SQL Servers ähnlich viele Details über den Zustand des Systems liefern wie die v$- bzw. dba/all/user-Views in Oracle (was nicht allzu sehr verwundern sollte, da die Speicherung interner Verwaltungsinformationen in Tabellenform meiner Erinnerung nach zu den Anforderungen Codds an ein RDBMS gehört). Die folgenden Analysequeries laufen alle auf Version 2008.

So kann man sich z.B. die teuersten Queries des Datenbank-Caches anzeigen lassen:

select top 50
       SUBSTRING(  st.text
                 , (qs.statement_start_offset/2) + 1
                 , ((CASE qs.statement_end_offset
                          WHEN -1 THEN DATALENGTH(st.text)
                          ELSE qs.statement_end_offset
                     END - qs.statement_start_offset)/2) + 1) AS StatementText
     , qs.last_execution_time
     , qs.execution_count
     , qs.total_elapsed_time/1000000 total_elapsed_sec
     , qs.last_elapsed_time/1000000 last_elapsed_sec
     , qs.total_logical_reads
     , qs.last_logical_reads
     , qp.query_plan
  from sys.dm_exec_query_stats qs
 cross apply sys.dm_exec_sql_text(qs.sql_handle) AS st  
 cross apply sys.dm_exec_query_plan(qs.plan_handle) AS qp  
 order by qs.last_elapsed_time desc

Oder die Queries, für die laut Optimizer Indizes fehlen:

select st.text
     , qp.query_plan
     , qs.*
  from Sys.dm_exec_query_stats qs
 cross apply sys.dm_exec_sql_text(qs.sql_handle) st  
 cross apply sys.dm_exec_query_plan(qs.plan_handle) qp  
 where convert(nvarchar(max), qp.query_plan) like '%MissingIndex%'

Oder die aktuell laufenden Abfragen:

select ses.login_name
     , ses.host_name
     , ses.program_name
     , st.text
     , req.session_id
     , req.start_time
     , req.status
     , req.command
     , req.blocking_session_id
     , req.cpu_time/1000 cpu_secs
     , req.total_elapsed_time/1000 ela_secs
     , req.logical_reads
     , qp.query_plan
  from sys.dm_exec_requests req
 inner join sys.dm_exec_sessions ses
    on req.session_id = ses.session_id
 cross apply sys.dm_exec_sql_text(req.sql_handle) AS st  
 cross apply sys.dm_exec_query_plan(req.plan_handle) AS qp

Oder auch die aktuell verbundenen Sessions

select ses.login_name
     , ses.*
  from sys.dm_exec_sessions ses
 where login_name not in ('sa', 'jira')
 order by ses.login_name

Detailliertere Erläuterungen zu den verwendeten DMVs findet man hier. Auf den untergeordneten Seiten findet man dort auch allerlei Analysequeries.

Mittwoch, Juni 16, 2010

MDX Scope

Mit MDX habe ich mich in den letzten beiden Jahren relativ viel beschäftigt, darüber hier aber noch kein Wort verloren. Dafür gab's mehrere Gründe, die aber - wenn überhaupt - ein andermal genannt werden sollen. Ein wichtiger Punkt ist allerdings, dass mir MDX nicht besonders liegt, und dass ich mir wichtige Details einfach nicht merken kann. Um dem abzuhelfen, versuche ich hier ein paar Notizen abzulegen.

Ein erstes Konzept, das mir regelmäßig Unbehagen verursacht, ist der SCOPE. Ein SCOPE ist ein Teilbereich eines Cubes (also ein Subcube), für den bestimmte Sonderregeln definiert werden. Man kann in ihm Überschreibungen festlegen, also dafür sorgen, dass Kennzahlen im SCOPE anders gefüllt werden als außerhalb des SCOPEs. Interessant ist dabei vor allem das unterschiedliche Verhalten von Basiskennzahlen und berechneten Kennzahlen (calculated measures). Dazu ein Beispiel. Im folgenden MDX-Script wird ein calculated measure test_calc definiert. Außerdem existiert im Cube eine Basiskennzahl test_mat, die zunächst mit NULL gefüllt ist. Beide Kennzahlen werden in einem SCOPE auf Artikelebene überschrieben:

CALCULATE; 

CREATE MEMBER CURRENTCUBE.[Measures].[test_calc]
 AS null, 
VISIBLE = 1  ; 

scope [DIM_PRODUCT].[ITEMID].[ITEMID].members; 
test_calc = 1;
test_mat = 1;
end scope;

In der Artikeldimension (DIM_PRODUCT) sind über Attributbeziehungen mehrere Ebenen definiert: über der Artikelebene liegen mehere Warengruppenebenen. Auf der Ebene der SCOPE-Definition, also auf der Artikelebene (ITEMID), liefern beide Kennzahlen den gleichen Wert:


Auf der übergeordneten Warengruppenebene (ITEMGROUPID LEVEL1) hingegen wird die Basiskennzahl weiter aggregiert, während für die berechnete Kennzahl kein Wert definiert ist:


Der SCOPE bestimmt den Ausschnitt des Würfels, in dem eine neue Berechnungslogik gilt, aber für Basiskennzahlen betrifft die Zuordnung alle höheren Ebenen, während für Calculations keine Aggregation von Werten erfolgt, sondern die Berechnung auf jeder Ebene durchgeführt wird.

Donnerstag, Juni 10, 2010

MINUS und EXCEPT

Noch ein Eintrag zum SQL Server: in dessen SQL-Dialekt fehlt das Schlüsselwort MINUS, mit dem man in Oracle die Elemente einer Menge bestimmen kann, die in der zweiten Menge fehlen. Allerdings fehlt tatsächlich nur das Schlüsselwort - ihm entspricht beim SQL Server EXCEPT:

-- Oracle
select 1 from dual
union
select 2 from dual
minus
select 2 from dual;

-- SQL Server
select 1
union
select 2
except
select 2

Mittwoch, Juni 09, 2010

MS SQL Kompression

nur als kurze Notiz: ähnlich wie in Oracle kann man auch im SQL Server das physikalische Speichervolumen einer Tabelle reduzieren: zum einen durch Verwendung der kleinsten geeigneten Datentypen (row compression) und darüber hinaus durch Ersetzung wiederkehrender Inhalte durch Platzhalter und die Verwendung einer Hash Map zur Auflösung dieser Ersetzungen (page compression). Die Komprimierung kann über die SSMS-GUI erfolgen oder auch über folgendes Kommando:

USE [Name_der_Datenbank]
ALTER TABLE [dbo].[Name_der_Tabelle] REBUILD PARTITION = ALL WITH (DATA_COMPRESSION = PAGE) 

Mittwoch, Juni 02, 2010

SQL*Net Compression

In Jonathan Lewis' Blog findet sich ein Beitrag zum Thema der SQL*Net Compression, die eine Reduzierung der Daten, die vom Server zum Client transportiert werden, ermöglicht. Grundsätzlich funktioniert das offenbar so, dass für jeden Satz entschieden wird, ob die Daten einer Spalte identisch mit den Inhalten der entsprechenden Spalte im letzten transferierten Satz sind - und auf diese Weise spielt die Sortierung der Daten eine wichtige Rolle für die Effizienz dieser Komprimierung (die eigentlich eine "de-duplication" ist). Interessanterweise kann ich die Ergebnisse nachvollziehen, wenn ich mit einer String-Spalte teste, wie sie Jonathan Lewis verwendet, also:

create table test
as
select rownum rn
     , lpad(dbms_random.string('U',2),40,'X')  v1
  from dual
connect by level < 10000;

-- Ergebnis:
-- 464825  bytes sent via SQL*Net to client - unsortiert
-- 118679  bytes sent via SQL*Net to client - sortiert nach v1

Wenn ich aber eine harmlose nummerische Spalte mit Wiederholungen verwende, sehe ich zwar auch Unterschiede zwischen der sortierten und der unsortierten Ausführung, aber sie gehen seltsamerweise in die andere Richtung:

create table test
as
select rownum rn
     , mod(rownum, 3)  v1
  from dual
connect by level < 10000;


-- Ergebnis:
-- 81653  bytes sent via SQL*Net to client - 
unsortiert
-- 81653  bytes sent via SQL*Net to client - sortiert nach rn


-- 90971  bytes sent via SQL*Net to client - sortiert nach v1

Mag sein, dass sich NUMBER-Werte bezüglich der Komprimierung tatsächlich anders verhalten. Oder mir entgeht noch irgendetwas Entscheidendes.

Nachtrag: nachdem mir auf Anhieb keine plausible Erklärung eingefallen war, habe ich Jonathan Lewis im Blog danach gefragt und folgende Antwort bekommen:

I think this may be a result of a fixed overhead for compression (or de-duplication, which might be a slightly better term). I think a row probably has to say “this column is the same as last time”, so I would look for a fixed size token to represent “same as last row”.
Test: create a table of 1,000,000 rows with a constant (e.g. 1000000001) and a variable (e.g. 1000000001 + rownum) column, set a large arraysize (5,000 is the max) then select each column in turn with autotrace traceonly statistics. See if the difference in volume is significant. It is, but there is a minimum volume of data per row which means you don’t see much of a saving with a single column of small numbers.
Meiner Erfahrung nach bekommt man auf thematisch passende Fragen in den Blogs meiner Sidebar immer gute Antworten (so war's jedenfalls auch bei Richard Foote und Charles Hooper).