Affichage des articles dont le libellé est oracle. Afficher tous les articles
Affichage des articles dont le libellé est oracle. Afficher tous les articles

lundi 30 mai 2011

Télécharger vos patchs Oracle avec wget

Vous l'avez peut être remarqué, mais Oracle vous donne à présent la possibilité de télécharger les archives en utilisant wget. Pour cela, il vous suffit de choisir "WGET options" après avoir cliqué sur download et ensuite de choisir download.sh pour récupérer le script. Enregistrer le sous le nom wget-oracle.sh.

Pour le faire fonctionner il vous reste à :

- entrer vos informations SSO, par exemple :
SSO_USERNAME=cscetbon@lesdatabases.com
SSO_PASSWORD=Waou000p
- commenter les instructions wget qui ne concernent pas les fichiers que vous désirez télécharger
- remplacer le ligne
WGET=/usr/bin/wget
par
WGET='/usr/bin/wget --user-agent="Mozilla/5.0" '
- ajouter en début de script l'instruction
export LANG=C
- rendre exécutable le script
chmod u+x wget-oracle.sh

Maintenant rendez vous dans un répertoire disposant d'assez d'espace pour télécharger vos archives et lancez la commande :
/le/chemin/que/vous/aurez/choisi/wget-oracle.sh

lundi 6 décembre 2010

Berkeley DB XML

Berkeley DB XML est une surcouche à Berkeley DB qui permet d'hériter de toutes ses fonctionnalités pour le stockage de documents XML.

En plus du stockage, BDB XML apporte les fonctionnalités suivantes :
- Indexation des noeuds, des attributs et des metadata
- Indexation au niveau noeud ou document
- QueryPlan
- Xquery 1.0
- Possibilité d'ajouter/requêter des metadata associées aux documents
- Documents modifiables (remplacer un sous ensemble du document peut être plus rapide)
- Validation (Il est possible de fournir un schema xsd qui est vérifié à l'insertion d'un document)
- Compression zlib par défaut (personnalisable)

XQUERY (XML Query Language) est disponible pour effectuer des recherches/modifications/ajouts de documents XML.
Il s'appuie sur Xqilla (bibliothèque C++) pour effectuer le parsing des requêtes Xquery/XPath.
Xqilla utilise Xerces (bibliothèque C++) pour effectuer le parsing et la validation des documents XML.
XQuery supporte les «Expressions FLWOR» (For/Let/Where/Order/Return) qui permettent d'effectuer toute sorte de traitements sur les données comme les boucles, les tris, le filtrage.
Il n'est pas encore possible de grouper les données avec XQuery 1.0, mais cette fonctionnalité founie par XQuery 1.1 sera certainement supportée dans l'une des prochaines versions de Berkeley DB XML. Pour plus d'information, vous pouvez vous rendre sur http://www.w3.org/TR/xquery.

Voyons quelques exemples simples de traitements qu'il vous est possible d'effectuer avec le language XQuery (dans l'ensemble des exemples qui suivent, vous pouvez utiliser l'interpréteur dbxml) :

- Pour ajouter un document au container test.dbxml
dbxml -h /var/tmp/
dbxml>createContainer c1.dbxml
dbxml> put p1 '<person name="pignon" age="32" Taille="160" genre="M">Charles</person>'
dbxml> put p2 '<person age="31" Taille="190" genre="M">Cyril Scetbon</person>'

Pour mettre à jour la valeur de l'attribut age du noeud person dont la valeur de l'attribut name vaut pignon
- replace value of node collection('c1.dbxml')/person[@name='pignon']/@age with '54'

Pour récupérer à partir du container c1.dbxml la valeur du noeud person dont la valeur de l'attribut Taille est supérieur à 180 et dont la valeur de l'attribut genre vaut M
- collection("c1.dbxml")/person[@Taille>180 and genre="M"]/text()
Cyril Scetbon

Pour compter le nombre de valeur distinctes de l'attribut Taille de tous les noeuds person contenus dans le container c1.dbxml
- count(distinct-values(collection("cont2.dbxml")/person/@Taille))
3

Et voici d'autres exemples utilisant des expressions FLWOR :

Pour afficher les chiffres de 1 à 10
- for $c in 1 to 3 return $c
1
2
3

Pour afficher le nom de tous les auteurs (valeur de l'attribut name du noeud author des documents du container authors.dbxml) qui ont écrit le livre dont le titre est 'MySQL 5 Administration et Optimisation' (vérification en faisant une jointure entre les containers books.dbxml et authors.dbxml sur la relation [valeur de l'attribut id du noeud /author = valeur de l'attribut id du noeud /book/author])
-for $book in collection("books.dbxml")/book[title='MySQL 5 Administration et Optimisation']
for $author in collection("authors.dbxml")/author[@id=$book/author/@id]
order by $author/name
return <author>{$author/name/string()}</author>
<author>Stephane Combaudon</author>
<author>Olivier Dasini</author>
<author>Cyril Scetbon</author>

Pour afficher l'ensemble des noeuds person du container c2.dbxml pour lesquels la valeur de l'attribut Taille est supérieur à 180 en les insérant entre des balises <response> </response>
- for $p in collection("c2.dbxml")/person[@Taille>180]
order by $p/@Taille descending
return <response>{$p}</respoonse>
<person genre="F" Taille="185">Brigitte Nielsen</person>
<person genre="M" Taille="182">Cyril Scetbon</person>


Dans un prochain article, nous verrons comment créer des index et vérifier que nos requêtes les utilisent bien.
Lire la suite...

jeudi 5 août 2010

Berkeley DB 5.0.26

Pour ceux qui ne connaissent pas Berkeley DB (BDB), ou qui en ont vaguement entendu parler, sachez que si vous êtes sous un système opensource, vous avez de grande chances d'utiliser BDB sans même le savoir.

Par exemple, si vous utilisez pidgin ou evolution vous utilisez BDB :

~$ lsof -n|grep 'libdb-'|awk '{print $1," ",$9}'|sort|uniq
evolution /usr/lib/libdb-4.8.so
pidgin /usr/lib/libdb-4.8.so

Je peux par exemple voir sur mon système le nombre de paquets qui ont déclaré dépendre de BDB :

~$ apt-cache rdepends libdb4.8| wc -l
96

On peut aussi en voir un aperçu :

~$ apt-cache rdepends libdb4.8| head
libdb4.8
Reverse Depends:
squidguard
libapache2-mod-php5filter
php5-cli
php5-cgi
openoffice.org-core
libpam-modules
libedata-cal1.2-6
libedata-book1.2-2

Et encore ce ne sont que les paquets qui l'ont déclaré ! Tout ça pour vous dire que BDB est indispensable à tout système open-source qui se respecte. Mais qu'est-ce que BDB ?

BDB est une bibliothèque permettant d'opérer sur des données (stockage, modification, recherche) et que l'on lie à une application pour lui fournir ce type de service. L'application peut être codée en utilisant des languages différents (JAVA, C++, C, Perl, PHP, Python, etc...) et choisi la structure la plus adaptée à ses données parmi les types Btree, Hash, Queue et Recno. (Vous pouvez consulter la documentation pour en savoir un peu plus sur ces différents types).

BDB supporte les transactions ACID, le multithreading et le multiprocessing, l'encryptage de l'environnement (répertoire stockant les données), l'indexation, les sauvegardes à chaud et la récupération des données en cas de crash (grâce à la journalisation des transactions), ainsi que la replication maître/esclaves !

Sachez qu'il est possible de configurer les environnements participant à un groupe de réplication de sorte que les rôles de maître/esclaves soient redistribués (failover) en cas d'erreur sur le maître ou au niveau du canal de communication. Il est à noter que pour utiliser la réplication il est nécessaire de développer du code c, c++ ou java :(
Oracle qui a racheté la société Sleepycat Software propriétaire de BDB en février 2006 annonce une capacité de stockage en teraoctets et des milliards d'enregistrements !!

Cette bibliothèque est disponible sous 2 licences :
  • commerciale
  • GPL (celle qui vous permet de l'utiliser sur votre système opensource)
Il est à noter qu'étant une bibliothèque, il n'y a pas de serveur de données. l'application accède et manipule directement les fichiers physiques grâce au code de la bibliothèque BDB. De même, ce n'est pas un RDBMS (relational database management system) ne supportant pas les relations entre les données stockées.

Dans un prochain article je vous montrerai comment installer et utiliser rapidement un environnement BDB XML. Soyez patients ...
Lire la suite...

mardi 12 mai 2009

Index et valeurs nulles

Une différence importante entre MySQL et Oracle est l'indexation. En effet, Oracle n'indexe pas les données entièrement nulles. Par entièrement cela signifie que si vous indexez 2 colonnes, le couple null ne sera pas stocké dans l'index et cela a son importance !

Par exemple, si nous utilisons le schema SCOTT pour tenter d'utiliser un index sur une colonne pouvant être nulle :

SQL> select * from emp;

EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10

14 rows selected.

SQL> create index idx_emp_ename on emp(ename);

Index created.

SQL> set autotrace trace explain
SQL> select 1 from emp where ename is null;

Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 1 | 7 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("ENAME" IS NULL)


Oracle décide donc d'effectuer un FULL SCAN de la table car la donnée nulle ne pouvant être stockée dans un index il est nécessaire de parcourir la table entièrement. Ce qui n'est pas le cas si on index une colonne supplémentaire non nulle (le couple ne sera dans ce cas jamais nul)

SQL> create index idx_emp_ename_1 on emp(ename,1);

Index created.

SQL> select 1 from emp where ename is null;

Execution Plan
----------------------------------------------------------
Plan hash value: 2365361045

------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 7 | 1 (0)| 00:00:01 |
|* 1 | INDEX RANGE SCAN| IDX_EMP_ENAME_1 | 1 | 7 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - access("ENAME" IS NULL)


On peut donc en déduire l'importance de rajouter la contrainte not null quand vous savez que ce champ ne peut être null. En effet, un count(*) pourra dans ce cas effectuer un INDEX FULL SCAN sachant qu'aucune donnée ne peut être nulle et donc il n'y a aucune entrée qui manque dans l'index.

Contrairement à Oracle, MySQL stocke aussi les valeurs nulles dans ses index comme l'indique la colonne NULL dans la sortie de la commande "SHOW INDEX FROM MATABLE". Ainsi si l'on recherche le nombre d'entrée nulles d'une table, MySQL utilisera l'index disponible :

mysql [localhost] {msandbox} (test) > explain select count(*) from t3 where id is null;
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
| 1 | SIMPLE | t3 | ref | id | id | 5 | const | 100 | Using where; Using index |
+----+-------------+-------+------+---------------+------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)

mysql [localhost] {msandbox} (test) > show index from t3;
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
| t3 | 1 | id | 1 | id | A | 1 | NULL | NULL | YES | BTREE | |
+-------+------------+----------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
1 row in set (0.00 sec)

De la même manière il est important d'ajouter la contrainte NOT NULL si le champ ne sera jamais nul, ce qui permet à MySQL d'effectuer certaines optimisations et d'économiser un bit par enregistrement.
Blogged with the Flock Browser
Lire la suite...

lundi 20 avril 2009

Oracle rachète SUN

En janvier 2008, SUN rachetait MySQL AB pour 1 milliard de $. Eh bien, aujourd'hui Oracle a annoncé sur  son site avoir accepté de racheter SUN pour 7 milliards de dollars. Il va falloir à nouveau s'interroger sur les conséquences, bonnes comme mauvaises, que cela pourra avoir pour les utilisateurs de MySQL.
Blogged with the Flock Browser

lundi 8 décembre 2008

Calcul de la médiane en SQL

Depuis Oracle 10g il est possible d'effectuer le calcul de la médiane très facilement en utilisant la nouvelle fonction MEDIAN disponible.
Je suppose que vous disposez d'une table data constituée de 2 colonnes semaine et duree. Pour calculer la médiane sous Oracle vous n'avez plus qu'à utiliser la requête suivante :

SELECT semaine,MEDIAN(duree) FROM data GROUP BY semaine;

Pour MySQL c'est beaucoup plus compliqué puisqu'on ne dispose pas de cette magnifique fonction. J'ai donc fait quelques recherches et je suis tombé sur les travaux de frédéric Brouard. J'ai pu trouver la procédure adéquate basée sur la solution de Chris Date.

En supposant que vous disposez toujours de la table data qui chez moi utilise le moteur CSV, la méthode est la suivante :

CREATE TABLE d AS SELECT * FROM data;
ALTER TABLE d ADD COLUMN id INT AUTO_INCREMENT PRIMARY KEY FIRST, ADD INDEX (semaine,duree) ;

SELECT semaine,round(AVG(duree),2) AS MEDIANE
FROM (
   SELECT semaine,MIN(duree) AS duree
   -- valeurs au dessus
   FROM
   (
       SELECT ST1.semaine,ST1.id, ST1.duree
       FROM    d AS ST1
       INNER JOIN d AS ST2
       ON ST1.duree <= ST2.duree AND ST1.semaine=ST2.semaine
       GROUP BY ST1.id, ST1.duree
       HAVING COUNT(*) <= (SELECT CEILING(COUNT(*) / 2.0)
       FROM d d2 where d2.semaine=ST1.semaine)
   ) SUR
   GROUP BY semaine
   UNION
   SELECT semaine,MAX(duree) AS duree
   -- valeurs en dessous
   FROM
   (
       SELECT ST1.semaine, ST1.duree
       FROM    d AS ST1
       INNER JOIN d AS ST2
       ON ST1.duree >= ST2.duree AND ST1.semaine=ST2.semaine
       GROUP BY ST1.id, ST1.duree
       HAVING COUNT(*) <= (SELECT CEILING(COUNT(*) / 2.0)
       FROM d d2 where d2.semaine=ST1.semaine)
   ) SOU
   GROUP BY semaine
   ) T
GROUP BY semaine;

DROP TABLE d;

Sous Oracle, la requête est très rapide et il n'est pas nécessaire de créer un index particulier. Par contre sous MySQL, si vous vous amusez à supprimer l'index vous le ressentirez très vite si vous disposez de beaucoup de données. Dans mon cas, ma table contient 4874 lignes. Sans l'index, la requête dure près de 5 minutes contre moins de 5 secondes une fois indexée.
Lire la suite...

lundi 28 juillet 2008

Query Cache MySQL Vs Oracle

Le Query Cache fait partie intégrante de MySQL depuis la version 3.23 (si ce n'est plus tôt). Il aura fallu attendre jusqu'à la version 11G pour voir arriver cette fonctionnalité chez Oracle. Nous allons tester cette fonctionnalité chez MySQL et Oracle et voir si le fait que l'on utilise le partitionnement apporte quelque chose.

Pour MYSQL (5.1 requis) :

CREATE TABLE table_mysql_partitionned(ident int unsigned, quand date, valeur int unsigned)
ENGINE=INNODB
PARTITION BY RANGE( YEAR(quand) )
(
PARTITION p2006 VALUES LESS THAN (2007),
PARTITION p2007 VALUES LESS THAN (2008),
PARTITION p2008 VALUES LESS THAN (2009),
PARTITION p2009 VALUES LESS THAN (2010)
);

INSERT INTO table_mysql_partitionned VALUES(1,'2008-06-04',20),(2,'2007-05-04',35),(3,'2008-07-04',3),(4,'2009-10-03',11),(1,'2009-12-25',46),
(2,'2008-10-12',43),(10,'2006-05-03',32),(5,'2007-06-12',56);


mysql> select * from table_mysql_partitionned where quand between '2008-01-01' and '2008-10-01';
+-------+------------+--------+
| ident | quand | valeur |
+-------+------------+--------+
| 1 | 2008-06-04 | 20 |
| 3 | 2008-07-04 | 3 |
+-------+------------+--------+
mysql> show global status like 'Qcache%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 20958592 |
| Qcache_hits | 4 |
| Qcache_inserts | 4 |
| Qcache_lowmem_prunes | 0 |
+-------------------------+----------+
8 rows in set (0,00 sec)

mysql> select * from table_mysql_partitionned where quand between '2008-01-01' and '2008-10-01';
+-------+------------+--------+
| ident | quand | valeur |
+-------+------------+--------+
| 1 | 2008-06-04 | 20 |
| 3 | 2008-07-04 | 3 |
+-------+------------+--------+
2 rows in set (0,00 sec)

mysql> show global status like 'Qcache%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 20958592 |
| Qcache_hits | 5 |
| Qcache_inserts | 4 |
| Qcache_lowmem_prunes | 0 |
+-------------------------+----------+
8 rows in set (0,01 sec)

mysql> INSERT INTO table_mysql_partitionned VALUES(1,'2009-06-04',34);
Query OK, 1 row affected (0,04 sec)

mysql> select * from table_mysql_partitionned where quand between '2008-01-01' and '2008-10-01';
+-------+------------+--------+
| ident | quand | valeur |
+-------+------------+--------+
| 1 | 2008-06-04 | 20 |
| 3 | 2008-07-04 | 3 |
+-------+------------+--------+
2 rows in set (0,00 sec)

mysql> show global status like 'Qcache%';
+-------------------------+----------+
| Variable_name | Value |
+-------------------------+----------+
| Qcache_free_blocks | 1 |
| Qcache_free_memory | 20959616 |
| Qcache_hits | 5 |
| Qcache_inserts | 5 |
| Qcache_lowmem_prunes | 0 |
+-------------------------+----------+
8 rows in set (0,00 sec)

La partition n'a pas été modifiée et pourtant le résultat du query cache a été invalidé :-(


Pour Oracle :

SQLPLUS SCOTT/****

CREATE TABLE table_oracle_partitionned(ident NUMBER, quand date, valeur NUMBER)
PARTITION BY RANGE (quand)
INTERVAL (numtoyminterval(1,'YEAR'))
(
PARTITION p2006 VALUES LESS THAN (to_date('2007-01-01','yyyy-mm-dd'))
);

INSERT INTO table_oracle_partitionned VALUES(1,to_date('2008-06-04','yyyy-mm-dd'),20);
INSERT INTO table_oracle_partitionned VALUES(2,to_date('2007-05-04','yyyy-mm-dd'),35);
INSERT INTO table_oracle_partitionned VALUES(3,to_date('2008-07-04','yyyy-mm-dd'),3);
INSERT INTO table_oracle_partitionned VALUES(4,to_date('2009-10-03','yyyy-mm-dd'),11);
INSERT INTO table_oracle_partitionned VALUES(1,to_date('2009-12-25','yyyy-mm-dd'),46);
INSERT INTO table_oracle_partitionned VALUES(2,to_date('2008-10-12','yyyy-mm-dd'),43);
INSERT INTO table_oracle_partitionned VALUES(10,to_date('2006-05-03','yyyy-mm-dd'),32);
INSERT INTO table_oracle_partitionned VALUES(5,to_date('2007-06-12','yyyy-mm-dd'),56);
EXIT;

SQLPLUS / AS SYSDBA
SQL> EXECUTE dbms_result_cache.flush;
SQL> SELECT * FROM scott.table_oracle_partitionned WHERE quand BETWEEN to_date('2008-01-01','yyyy-mm-dd') AND to_date('2008-10-01','yyyy-mm-dd');

IDENT QUAND VALEUR
---------- -------- ----------
1 04/06/08 20
3 04/07/08 3

Si on lance plusieurs fois la requête et que l'on regarde les informations du result_cache :

SQL> select name, value from v$result_cache_statistics;

NAME VALUE
-------------------- ----------
Create Count Success 1
Create Count Failure 0
Find Count 4
Invalidation Count 0
Delete Count Invalid 0

On s'aperçoit que le résultat est bien récupéré à partir du result_cache.

J'effectue une mise à jour sur la table mais en prenant soin de ne pas intervenir sur les enregistrements de la partition contenant les données de l'année 2008 :

SQL> UPDATE scott.table_oracle_partitionned SET VALEUR=VALEUR+1 WHERE quand BETWEEN to_date('2007-01-01','yyyy-mm-dd') AND to_date('2007-12-30','yyyy-mm-dd');

2 rows updated.

Je relance la même requête pour récupérer les enregistrements de l'année 2008 et je regarde à nouveau les statistiques du result_cache :

SQL> SELECT name, value FROM v$result_cache_statistics;

NAME VALUE
-------------------- ----------
Create Count Success 2
Create Count Failure 0
Find Count 4
Invalidation Count 1
Delete Count Invalid 0

En conclusion :

Le Result Cache d'Oracle se comporte comme le Query Cache de MySQL dans le sens où chaque modification d'une table engendre l'invalidation des SELECTs concernés. J'espérais qu'Oracle aurait su prendre en compte le partitionnement de la table pour obtenir une invalidation plus fine ce qui dans l'exemple plus haut aurait eu pour conséquence de ne pas recréer un enregistrement dans le Result Cache.