Archive

Archive for the ‘PostgreSQL’ Category

PostgreSQL, dane binarne i dumpy

November 14th, 2008 1 comment
Zauważyłem nowa fajną cechę w PostgreSQL. Otóż potrafi on wykonać tekstowy zrzut bazy danych, która zawiera dane binarne. Czyli, mam w tabelkach PostgreSQL pliki wgrane przez użytkowników. Robię pg_dump i wszystko działa poprawnie. Kiedyś tak nie było i dlatego teraz się cieszę.
Categories: PostgreSQL Tags:

Zend Framework i keszowanie zapytań do bazy danych

June 23rd, 2007 3 comments
Witam, Od pewnego czasu programiści PHP naśladują programistów Javy, sam zresztą to robię. Efektem tego jest przenoszenie wzorców projektowych z Javy do PHP. Na przykład Zend Framework a w szczególności jego kod odpowiedzialny za bazy danych – Zend_Db (działający prawie jak ORM) przypomina w pewien sposób to, za co świat Javy pokochał Hibernate. Konsekwencją użycia ORM w projekcie jest to, że spada nam złożoność zapytań do bazy danych, natomiast zwiększa się ich ilość. Hibernate potrafi keszować obiekty. Zend Framework niestety nie. Jednak wczoraj tego bardzo potrzebowałem, renderowanie prostej strony WWW wymagało 500 zapytań do bazy, grrr, z czego 450 było powtórzeniem poprzednich zapytań. O ile keszowanie obiektów było by bardzo skomplikowane, to w miarę prosto można było keszować wyniki zapytań SQL w obrębie jednego żądania HTTP. Jak się do tego zabrać? Zauważyłem, że wszystkie zapytania do bazy danych produkowane przez Zend Framework w pewnym momencie trafią do metody query obiektu Zend_Db_Adapter_*, fajnie:). Naszą klasę keszującą trzeba odziedziczyć po tej klasie. Oczywiście można to zrobić na wiele innych sposobów, i bardzo ładnie wpasować w to wzorzec projektowy dekorator, ale ja nie miałem czasu. Modyfikujemy metodę query, tak by zapamiętywała swoje wyniki i trzymała w keszu. U mnie działa coś takiego (wstydził bym się tego kodu, gdyby nie to że robi coś na prawdę przydatnego):
class JakubiakDbAdapterPdoPgsql extends Zend_Db_Adapter_Pdo_Pgsql{
  public function query($sql, $bind = array()){
    $sql = $sql . ""; // a sprobuj no bez tego
    $hash = $this->_calculateHash($sql,$bind);
    // czy uda nam sie pobrac z kesza
    if($this->_isCacheEnabled) {
      if($this->_allowCache($sql,$bind)) {
        if(isset($this->_cachedResults[$hash])) {
          return $this->_cachedResults[$hash];  
        } 
      }
    }
    $res = parent::query($sql, $bind);
    // dopisanie do kesza
    if($this->_isCacheEnabled) {
      $this->_cachedResults[$hash] = $res;
    }
    return $res;
  }
  // pozwalam na keszowanie tylko zapytan rozpoczynajacych sie od select
  private function _allowCache($sql,$bind) {
    return preg_match("/^select/i",$sql));
  }
  private function _calculateHash($sql,$bind) {
    return md5($sql.var_export($bind,true));
  }
  [...]
}
Ten kod omal nie zadziała. Jednak trzeba zrobić jeszcze parę myków. Przeciążyć konstruktor naszego adaptera.
  public function __construct($config) {
    parent::__construct($config);
    $this->getConnection()->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('JakubiakDbStatement', array($this)));
  }
I dopisać taką oto koszmarną klasę:
class JakubiakDbStatement extends PDOStatement {
  $_cache = null;
  public function fetchAll($how, $class_name=null, $ctor_args=null) {
    if(!empty($this->_cache)) {
      return $this->_cache;
    }
    $this->_cache = parent::fetchAll($how);
    return $this->_cache;
  }
}
Ten kod, ma prawo nie działać i zawiera błędy. Jednak u mnie działa. I to dobrze działa. Zamiast 500 zapytań do bazy mam ich 50. Serwer jest mi za to wdzięczny, nie trzeba już oliwić wentylatorka od chłodzenia;). PS. do testów użyłem starej wersji Zend_Framework – 0.9.1.
Categories: ORM, PHP, PostgreSQL, Zend Framework Tags:

Zend Framework i PostgreSQL aka ORM

April 21st, 2007 No comments
Proponuje kilka moich poprawek do Zend Framework 0.9.2. Dzięki temu praca z tą biblioteką jest jeszcze przyjemniejsza. Wersja 0.9.2 ma parę niedociągnięć, jednak można je poprawić poprzez dziedziczenie. Zacznę od klasy Zend_Db_Adapter_Pdo_Pgsql PostgreSQL. Pisałem już o niej. Proponuje następujące ulepszenia:
class JakubiakDbAdapterPdoPgsql extends Zend_Db_Adapter_Pdo_Pgsql{
  public function query($sql, $bind = array()){
    // poprawienie błędu dla typu Boolean
    if(is_array($bind)){
      foreach($bind as $k => $v) {
        if(is_bool($v)) {
          $bind[$k] = $v ? 't' : 'f';
        }
      }
    }
    return parent::query($sql, $bind);
  }
  public function lastInsertId($tableName = null) {
    // Jeżeli sekwencja nazywasz inaczej niż domyślnie, to prawdopodobnie trzeba
    // będzie nadpisać też tą funkcję, u mnie wygląda ona tak 
    // sekwencje dla kluczy głównych nazywam {nazwa_tabelki}_seq
    if (!$tableName) {
      throw new Zend_Db_Adapter_Exception("Sequence name must be specified");
    }
    $this->_connect();
    $sequenceName = "{$tableName}_seq";
    $sequenceName = preg_replace('/_+/','_',$sequenceName);
    return $this->_connection->lastInsertId($sequenceName);
  }
Teraz zabawię się z klasą która reprezentuje tabelę Zend_Db_Table. Jest ona podwaliną ORM, dlatego mi się podoba:
class JakubiakDbTable extends Zend_Db_Table {
  // chcę, aby wszystkie wiersze w tabelkach były reprezentowane przez
  // obiekty tej klasy
  protected $_rowClass = 'JakubiakDbTableRow';
  // następnie poprawiam błąd - lastInsertedId
  public function insert(array $data)  {
    $this->_db->insert($this->_name, $data);
    return $this->_db->lastInsertId(empty($this->_seqName)
      ? $this->_name : $this->_seqName);
  }
  // przydatna funkcja do zliczania wierszy w tabeli
  public function count(array $where = array()) {
    $select = $this->_db->select();
    $select->from($this->_name, array('count'=>'count(*)'));
    $where = (array) $where;
    foreach ($where as $key => $val) {
      if (is_int($key)) {
        $select->where($val);
      } else {
        $select->where($key, $val);
      }
    }
    $select->limit(1, 0);
    $stmt = $this->_db->query($select);
    $data = $stmt->fetch();
    return empty($data['count']) ? 0 : intval($data['count']);
  }
  // pobieranie nowej encji
  public function fetchNew() {
    $newRow = parent::fetchNew();
    $newRow->loadDefaults();
    return $newRow;
  }
  // to się jeszcze przyda, aczkolwiek wydaje mi się, że ta funkcja trafi prędzej czy 
  // później do core
  public function getReferenceMap($key){
    if(empty($this->_referenceMap[$key])){
      return null;
    }
    return $this->_referenceMap[$key];     
  }
}
I jeszcze klasa która reprezentuje encje Zend_Db_Table_Row – lub jak kto woli – wiersz w tabeli:
class JakubiakDbTableRow extends Zend_Db_Table_Row {
  // czy jesteśmy nową encją?
  private $_isNew = false;
  // wczytanie domyślnych wartości, na podstawie metadanych zapisanych w tabeli
  public function loadDefaults() {
    $db = $this->_getTable()->getAdapter();    
    $info = $this->_getTable()->info();
    foreach($info['metadata'] as $col => $meta){
      if(empty($meta['DEFAULT'])) continue;
      $default = $meta['DEFAULT'];
      $one = $db->query("select $default as def")->fetch();
      $this->_data[$col] = $one['def'];
    }
    $this->_isNew = true;
  }
  // zapisywanie rekordu do bazy, kod prawie taki sam jak w klasie nadrzędnej,
  // ale wykorzystuje prywatną zmienną isNew
  public function save() {
    $keys = $this->_getPrimaryKey();
    $values = array_filter($keys);
    if ($this->_isNew) {
      $this->_insert();
      $result = $this->_getTable()->insert($this->_data);
      if (is_numeric($result)) {
        $this->_data[key($keys)] = $result;
        $this->_refresh();
      }
    } else {
      $where = $this->_getWhereQuery(false);
      $this->_update();
      $depTables = $this->_getTable()->getDependentTables();
      if (!empty($depTables)) {
        $db = $this->_getTable()->getAdapter();
        $pkNew = $this->_getPrimaryKey(true);
        $pkOld = $this->_getPrimaryKey(false);
        $thisClass = get_class($this);
        foreach ($depTables as $tableClass) {
          Zend_Loader::loadClass($tableClass);
          $t = new $tableClass(array('db' => $db));
          $t->_cascadeUpdate($this->getTableClass(), $pkOld, $pkNew);
        }
      }
      $result = $this->_getTable()->update($this->_data, $where);
      if (is_int($result)) {
        // update worked, refresh with data from the table
        $this->_refresh();
      }
    }
    return $result;
  }
  // pobieranie many to one w bardziej intuicyjny sposób
  public function __get($key) {
    $r = $this->_getTable()->getReferenceMap($key);
    if (!empty($r)) {
      // tu można dopisać loader dla klasy 
      return $this->findParentRow($r['refTableClass'],$key);
    }
    return parent::__get($key);
  }
  // domyślnie setter zabrania zmieniać wartości klucza głównego, 
  // ale czasami chcę to robić, kod prawie taki sam jak rodzica
  public function __set($key, $value) {
    if (!$this->_isNew && in_array($key, $this->_primary)) {
      require_once 'Zend/Db/Table/Row/Exception.php';
      throw new Zend_Db_Table_Row_Exception("Changing the primary key value(s) is not allowed");
    }
    if (!array_key_exists($key, $this->_data)) {
      require_once 'Zend/Db/Table/Row/Exception.php';
      throw new Zend_Db_Table_Row_Exception("Specified column \"$key\" is not in the row");
    }
    $this->_data[$key] = $value;
  }
Po tej zabawie, mogę używać bibliotek na przykład tak:
$fileTable = new FileTable();
$fileRow = $fileTable->fetchNew($_FILES[$filename]);
$fileRow->save();
$mediaTable = new MediaTable();
$mediaRow = $mediaTable->fetchNew();
$mediaRow->mediatitle = $request->getParam('mediatitle');
$mediaRow->mediadescription = $request->getParam('mediadescription');
$mediaRow->fileid = $fileRow->fileid;
$mediaRow->userid = UserTable::getFromAuth()->userid;
$mediaRow->save();
$photoTable = new PhotoTable();
$photoRow = $photoTable->fetchNew();    
$photoRow->mediaid = $mediaRow->mediaid;
$photoRow->photowidth = $width;
$photoRow->photoheight = $height;
$photoRow->save();
echo $photoRow->media->file->fileid;
Miodzio? Nie chcę wracać do czasów, gdy nie znałem ORM. Czuję się prawie jak w JPA. Acha, nie zapomnij o przeczytaniu dokumentacji Zenda bo jest bardzo dobra.
Categories: ORM, PHP, PostgreSQL, Zend Framework Tags:

PostgreSQL + Vista – User Account Control

February 22nd, 2007 2 comments
Instalacja bazy danych PostgreSQL 8.2 na Windows Vista może być bardziej kłopotliwa, niż na wcześniejszych wersjach Windowsa. Aby przebiegała bez problemu polecam wyłączyć “User Account Control” w “Windows Security Center”. Jeżeli jest to nasza kolejna próba instalacji, to warto też usunąć z systemu użytkownika “postgres” i docelowy katalog instalacji. Po tych zabiegach instalacja PostgresSQL będzie przebiega gładko. Podobnie jest z serwerem Apache 2.2.
Categories: PostgreSQL Tags:

PostgreSQL 8.2 vs 8.1 – nowa wersja jest szybsza

December 7th, 2006 No comments
Witam, Przeprowadziłem prymitywne porównanie szybkości działania aplikacji WWW działającej z bazą danych PostgreSQL w wersji 8.1 i 8.2. Zgodnie z zapowiedziami, nowa wersja jest szybsza. W moim prostym teście wzrost szybkości dla całej aplikacji WWW sięgał nawet 60%. Brawo!!! Nowa wersja PostgreSQL przychodzi z konfiguracją bardziej zachłannie korzystającą z pamięci. Wykonałem więc test serwera 8.2 z ustawieniami pamięci tak, jak było w 8.1. 60% przyrost szybkości został zachowany. Brawo^2 !!! Metoda testowa. Korzystałem z jednego fizycznego serwera pod kontrolą systemu FreeBSD. Bazę danych PostgreSQL kompilowałem ze źródeł, dostępnych w portach systemu. Najpierw zainstalowałem i testowałem na bazie w wersji 8.1. Następnie ją odinstalowałem i zainstalowałem bazę 8.2. Przy użyciu webbench symulowałem 10 internautów. Aplikacja wykonywała zapis do bazy i kilkanaście prostych selektów i jeden potężny select. Moje testy uznaje za prymitywne. Jednak jak dla mnie wystarczające. Brawo nowy PostgreSQL!!!
Categories: PostgreSQL Tags:

GiST vs btree podczas wyszukiwania wartości w przedziale

September 21st, 2006 2 comments
Programując bazę danych PostgreSQL często korzystam z wyszukiwania wartości w przedziale – to znaczy czy wartość X jest pomiędzy A i B. Aby zoptymalizować tego typu zapytanie można założyć indeks dwukolumnowy na btree( A, B ). Dziś spróbowałem jednak czegoś innego – indeksu GiST. W tym celu wykorzystałem bibliotego SEG z katalogu contrib w PostgreSQL. Nie opłaciło się to. W tabelce A mam dwa miliony wylosowanych rekordów. Dla każdego wiersza A<B i S=’a..b’. Przeprowadziłem badania dla początku, środka i końca przedziału wyznaczonego przez minimalne i maksymalne wartości A i B. Czas wyszukiwania dla zapytań z użyciem indeksu btree i GiST są podobne.
htest=# \d a
          Table "public.a"
Column |       Type       | Modifiers
--------+------------------+-----------
a      | double precision |
b      | double precision |
dif    | double precision |
s      | seg              |
Indexes:
   "a_ab" btree (a, b)
   "a_s" gist (s)


htest=# SELECT count(*), min(a) as mina, max(a) as maxa, min(b) as minb, max(b) as maxb from a;
 count  |         mina         |       maxa       |         minb         |       maxb      
---------+----------------------+------------------+----------------------+------------------
2000001 | 1.68569386083898e-07 | 0.99999854248017 | 0.000242068432384202 | 1.99908247962551



htest=# SELECT count(*) from a where s @ '0.001'::seg;
count
-------
 1974
(1 row)

htest=# SELECT count(*) from a where 0.001 between a and b;
count
-------
 1974
(1 row)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where s @ '0.001'::seg;
                                                       QUERY PLAN                                                        
-------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=5694.69..5694.70 rows=1 width=0) (actual time=42.009..42.011 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=22.00..5689.68 rows=2000 width=0) (actual time=4.066..37.296 rows=1974 loops=1)
         Recheck Cond: (s @ '0.001'::seg)
         ->  Bitmap Index Scan on a_s  (cost=0.00..22.00 rows=2000 width=0) (actual time=3.159..3.159 rows=1974 loops=1)
               Index Cond: (s @ '0.001'::seg)
 Total runtime: 42.097 ms
(6 rows)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where 0.001 between a and b;
                                                       QUERY PLAN                                                       
------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=736.79..736.80 rows=1 width=0) (actual time=37.937..37.939 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=3.20..736.29 rows=200 width=0) (actual time=1.832..34.346 rows=1974 loops=1)
         Recheck Cond: ((0.001::double precision >= a) AND (0.001::double precision <= b))
         ->  Bitmap Index Scan on a_ab  (cost=0.00..3.20 rows=200 width=0) (actual time=0.923..0.923 rows=1974 loops=1)
               Index Cond: ((0.001::double precision >= a) AND (0.001::double precision <= b))
 Total runtime: 38.020 ms
(6 rows)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where s @ '0.01'::seg;
                                                         QUERY PLAN                                                         
----------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=5694.69..5694.70 rows=1 width=0) (actual time=330.687..330.689 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=22.00..5689.68 rows=2000 width=0) (actual time=40.345..294.110 rows=19677 loops=1)
         Recheck Cond: (s @ '0.01'::seg)
         ->  Bitmap Index Scan on a_s  (cost=0.00..22.00 rows=2000 width=0) (actual time=30.849..30.849 rows=19677 loops=1)
               Index Cond: (s @ '0.01'::seg)
 Total runtime: 330.906 ms
(6 rows)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where 0.01 between a and b;
                                                          QUERY PLAN                                                           
-------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=17913.90..17913.91 rows=1 width=0) (actual time=337.855..337.856 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=181.55..17868.26 rows=18256 width=0) (actual time=31.973..301.142 rows=19677 loops=1)
         Recheck Cond: ((0.01::double precision >= a) AND (0.01::double precision <= b))
         ->  Bitmap Index Scan on a_ab  (cost=0.00..181.55 rows=18256 width=0) (actual time=15.159..15.159 rows=19677 loops=1)
               Index Cond: ((0.01::double precision >= a) AND (0.01::double precision <= b))
 Total runtime: 338.070 ms
(6 rows)
htest=# EXPLAIN ANALYZE SELECT count(*) from a where s @ '0.1'::seg;
                                                          QUERY PLAN                                                           
-------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=5694.69..5694.70 rows=1 width=0) (actual time=1239.315..1239.316 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=22.00..5689.68 rows=2000 width=0) (actual time=189.930..898.891 rows=189570 loops=1)
         Recheck Cond: (s @ '0.1'::seg)
         ->  Bitmap Index Scan on a_s  (cost=0.00..22.00 rows=2000 width=0) (actual time=164.654..164.654 rows=189570 loops=1)
               Index Cond: (s @ '0.1'::seg)
 Total runtime: 1239.599 ms
(6 rows)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where 0.1 between a and b;
                                                             QUERY PLAN                                                             
------------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=22163.00..22163.01 rows=1 width=0) (actual time=1198.103..1198.105 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=2002.74..21663.96 rows=199615 width=0) (actual time=154.321..854.816 rows=189570 loops=1)
         Recheck Cond: ((0.1::double precision >= a) AND (0.1::double precision <= b))
         ->  Bitmap Index Scan on a_ab  (cost=0.00..2002.74 rows=199615 width=0) (actual time=130.457..130.457 rows=189570 loops=1)
               Index Cond: ((0.1::double precision >= a) AND (0.1::double precision <= b))
 Total runtime: 1198.389 ms
(6 rows)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where s @ '1'::seg;
                                                          QUERY PLAN                                                           
-------------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=5694.69..5694.70 rows=1 width=0) (actual time=5003.059..5003.060 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=22.00..5689.68 rows=2000 width=0) (actual time=801.516..3216.648 rows=999981 loops=1)
         Recheck Cond: (s @ '1'::seg)
         ->  Bitmap Index Scan on a_s  (cost=0.00..22.00 rows=2000 width=0) (actual time=780.752..780.752 rows=999981 loops=1)
               Index Cond: (s @ '1'::seg)
 Total runtime: 5003.345 ms
(6 rows)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where 1 between a and b;
                                                     QUERY PLAN                                                     
--------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=49189.14..49189.15 rows=1 width=0) (actual time=5087.386..5087.387 rows=1 loops=1)
   ->  Seq Scan on a  (cost=0.00..46667.01 rows=1008848 width=0) (actual time=10.998..3317.487 rows=999981 loops=1)
         Filter: ((1::double precision >= a) AND (1::double precision <= b))
 Total runtime: 5087.446 ms
(4 rows)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where s @ '1.999'::seg;
                                                      QUERY PLAN                                                      
----------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=5694.69..5694.70 rows=1 width=0) (actual time=0.347..0.349 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=22.00..5689.68 rows=2000 width=0) (actual time=0.331..0.334 rows=1 loops=1)
         Recheck Cond: (s @ '1.999'::seg)
         ->  Bitmap Index Scan on a_s  (cost=0.00..22.00 rows=2000 width=0) (actual time=0.302..0.302 rows=1 loops=1)
               Index Cond: (s @ '1.999'::seg)
 Total runtime: 0.431 ms
(6 rows)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where 1.999 between a and b;
                                                       QUERY PLAN                                                       
------------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=20499.44..20499.45 rows=1 width=0) (actual time=422.610..422.612 rows=1 loops=1)
   ->  Index Scan using a_ab on a  (cost=0.00..20498.93 rows=200 width=0) (actual time=422.263..422.590 rows=1 loops=1)
         Index Cond: ((1.999::double precision >= a) AND (1.999::double precision <= b))
 Total runtime: 422.681 ms
(4 rows)

Zwracam jednak uwagę, na wyniki ostatniego zapytania, o wartości z końca przedziału. W tym wypadku indeks GiST był 1000 razy szybszy. Założyłem więc dodatkowy indeks btree( B ) i potestowałem wartości z końca przedziału. Tadnem indeksów btree był tak samo lub nawet minimalnie szybszy niż indeks GiST.
htest=# SELECT count(*) from a where s @ '1.995'::seg;
 count 
-------
    16
(1 row)

htest=# EXPLAIN ANALYZE SELECT count(*) from a where s @ '1.995'::seg;
                                                      QUERY PLAN                                                       
-----------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=5694.69..5694.70 rows=1 width=0) (actual time=0.278..0.280 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=22.00..5689.68 rows=2000 width=0) (actual time=0.192..0.245 rows=16 loops=1)
         Recheck Cond: (s @ '1.995'::seg)
         ->  Bitmap Index Scan on a_s  (cost=0.00..22.00 rows=2000 width=0) (actual time=0.179..0.179 rows=16 loops=1)
               Index Cond: (s @ '1.995'::seg)
 Total runtime: 0.363 ms
(6 rows)

htest=# EXPLAIN ANALYZE select count(*) from a where 1.995 between a and b;
                                                     QUERY PLAN                                                      
---------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=736.29..736.30 rows=1 width=0) (actual time=0.134..0.136 rows=1 loops=1)
   ->  Bitmap Heap Scan on a  (cost=2.70..735.79 rows=200 width=0) (actual time=0.047..0.102 rows=16 loops=1)
         Recheck Cond: (1.995::double precision <= b)
         Filter: (1.995::double precision >= a)
         ->  Bitmap Index Scan on a_b  (cost=0.00..2.70 rows=200 width=0) (actual time=0.028..0.028 rows=16 loops=1)
               Index Cond: (1.995::double precision <= b)
 Total runtime: 0.209 ms
(7 rows)
Po przeprowadzeniu testów mam następujące wnioski:
  1. Sprawdź, jaki typ indeksu najbardziej odpowiada danym, które masz w tabeli
  2. Dla moich losowych wygenerowanych danych najlepszy był tandem indeksów btree( A, B ) i btree( B ).
Categories: PostgreSQL Tags: