SQL cosmétique

Samedi 11 juin 2011 6 11 /06 /Juin /2011 12:19

Bon, l'autre jour, mon chef me demandait : sais tu pallindromer une chaîne de caractères sans fonction PL ?

 

... merci google, il y a une built-in fonction :)

 

1) Fonction REVERSE

 

Attendez, un petit jeu de test d'abord :

 

SQL> CREATE TABLE testrev(txt VARCHAR2(255));

Table crÚÚe.

SQL> INSERT INTO testrev VALUES ('marcel');

1 ligne crÚÚe.

SQL> INSERT INTO testrev VALUES ('robert');

1 ligne crÚÚe.

SQL> INSERT INTO testrev VALUES ('cosmetic sql is good for you');

1 ligne crÚÚe.

SQL> commit;

Validation effectuÚe.

 

Et hop :

 

SQL> SELECT txt, reverse(txt) rev
  2  FROM testrev;

TXT                                                REV

marcel                                             lecram
robert                                             trebor
cosmetic sql is good for you                       uoy rof doog si lqs citemsoc

 

Surprise, ça marche !

Mais bon, c'est pas très drôle, disons le.

EDIT :  et en plus la fonction n'est pas supportée, et elle est limitée dans un certain nombre de cas (Cf. le commentaire de Laurent)

 

Ben on va essayer de le faire à la main quand même !

 

2) Encore de la clause MODEL !

 

Alors, dans la mesure où on est capable de faire de l'itératif avec la clause MODEL, on devrait y arriver :

Il suffit d'itérer, pour chaque ligne, sur la longueur de la chaine, et construire le résultat.

 

SQL>  SELECT txt, rev
  2   FROM (
  3      SELECT txt, length(txt) lng, length(txt) cnt
  4      FROM testrev
  5   ) t
  6   MODEL
  7   PARTITION BY (ROWID)
  8   DIMENSION BY (0 n)
  9   MEASURES (txt, lpad(' ', lng) rev, lng, cnt)
 10   RULES
 11   ITERATE(1000000) UNTIL (cnt[0] = 0)
 12   (
 13      rev[0] = substr(rev[0], 1, iteration_number) || substr(txt[0], lng[0] - iteration_number, 1)
 14      , cnt[0] = cnt[0] -1)
 15   /

TXT                                                REV

cosmetic sql is good for you                       uoy rof doog si lqs citemsoc
marcel                                             lecram
robert                                             trebor

 

La question que je me pose encore cela dit : mais à quoi ça sert de pallindromer ?

Pfiou, heureusement que ce blog ne sert à rien...

 

Par Pacman - Publié dans : SQL cosmétique
Ecrire un commentaire - Voir les 1 commentaires
Mercredi 25 mai 2011 3 25 /05 /Mai /2011 07:25

Salut les gars, moi c'est Robert, et je n'aime pas les mouches.

(Il me fallait une phrase d'intro, j'ai pas trouvé mieux...)

 

Bon aujourd'hui, c'est le deuxième exo de l'Euler challenge !

 

 http://projecteuler.net/index.php?section=problems


DISCLAIMER :

J'ai la flemme de m'inscrire sur le site, donc je ne prendrais même pas la peine de vérifier mes résultats


  By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.

 

1) La méthode brutale

 

Il y a quelques temps, je découvrais la clause MODEL qui permet de faire de l'itératif à partir de 10g... et donc facilement des suites récurrentes.

 

Donc du coup, c'est tout simple :

 

SQL> SELECT sum(u)
  2  FROM (
  3       SELECT n, u
  4       FROM (
  5         SELECT 0 n, 0 U FROM DUAL
  6         UNION ALL SELECT 1 n, 1 U FROM DUAL
  7         ) t
  8       MODEL
  9        dimension by (n)
 10        measures (u)
 11        rules upsert
 12        iterate(33)
 13        (u[2+iteration_number] = u[1+iteration_number] + u[iteration_number])
 14  )
 15  WHERE mod(u, 2) = 0
 16  /

    SUM(U)
----------
  4613732

 

Passionnant, n'est ce pas... allez, on va rigoler un peu plus !

 

2) La méthode mathématique

 

Bon, faire la somme de trucs qui sont déjà définis en se sommant les uns les autres, ça sent l'astuce à plein nez, non ?

(J'ai toujours fait les maths à l'intuition, ce qui explique surement mon destin pitoyable...)

 

Première remarque :

Un+2 = Un+1 + Un

Pour que Un+2 soit pair, il faut que Un+1 et Un soient soit tous les deux pairs, soit tous les deux impairs.

Mais s'ils sont tous les deux pairs, tous les autres termes le sont... et ce n'est pas le cas.

 

Bref, regardez bien les termes de la suite :

0 1 1 2 3 5 8 13 21 34 ...

Un terme sur 3 est pair, et bien sûr il est la somme des deux termes impairs précédents.

 

=> Si on s'arrête au bon endroit, la somme des termes pairs est égale à la somme des termes impairs !

 

La proposition formelle P(p) :

Quel que soit p, somme(U3i, 0<= i <= p) = somme(Ui impairs, 0<=i<=3p)

 

La preuve :

P(0) vraie, disons-le (ça c'est de l'argument !)

 

Supposons p(n) vraie, qu'en est-il de p(n+1) ?

 

somme(U3i, 0<= i <= n+1) = somme(U3i, 0<= i <= n)  + U3(n+1)

                                                 = somme(U3i, 0<= i <= n)  + U3n+1 + U3n+2

                                                 = somme(Ui impairs, 0<= i <= 3n)  + U3n+1 + U3n+2

                                                 = somme(Ui impairs, 0<= i <= 3(n+1))

 

Ca, c'est fait... 

 

somme(U3i, 0<= i <= n) = somme(Ui, 0<= i <= 3n) / 2

 

Il ne reste plus qu'à calculer la "série" de Fibonacci !

Pour cela, deuxième remarque :

quand on somme des termes qui justement se définissent comme la somme des deux précédents, on doit avoir des chances de retomber sur un de ces termes, non ?

 

Fibo 0 1 1 2 3 5 8 13 21
Série 0 1 2 4 7 12 20 33 54

 

Ben on retombe pas tout à fait dessus, mais à un près...

Somme(Ui, 0<=i<=n) = Un+2 - 1

 

(La démo par récurrence est triviale, vous la ferez vous-même !)

 

Et du coup, le résultat (en utilisant la forme analytique de Fibonacci) : 

 

somme(U3i, 0<= i <= n) = (U3n+2 - 1) / 2 = ((((1 + sqrt(5)) / 2) ^ (3n+2) - ((1 - sqrt(5)) / 2) ^ (3n+2)) / sqrt(5) - 1) / 2

 

(Ok, c'est illisible...)

 

SQL> SELECT ((power((1+sqrt(5))/2, 35) - power((1-sqrt(5))/2, 35)) / sqrt(5) - 1) / 2 res
  2  FROM dual
  3  /

       RES
----------
   4613732

 

Bon ok, le U33, je l'ai cherché à tâtons et c'est laid, mais bon !

Par Pacman - Publié dans : SQL cosmétique
Ecrire un commentaire - Voir les 0 commentaires
Jeudi 19 mai 2011 4 19 /05 /Mai /2011 20:04

Bon début, d'une longue, longue série...

 

Le challenge Nr. 1 :

 

 
Ho, facile !
 
1) La méthode brute

Ben en SQL il faut :
- générer les 1000 premier entiers
(
  - filtrer les non multiples de 3 et / ou de 5 et sommer
OU
  - sommer en annulant les non multiples de 3 et / ou de 5
)
Sans discussion, la deuxième solution est la plus proche de la philosophie du PacBlog !
 
SQL> SELECT sum(level * greatest(0, 1 - mod(level, 3) * mod(level, 5)))
  2  FROM DUAL
  3  CONNECT BY level <= 1000
  4  /

SUM(LEVEL*GREATEST(0,1-MOD(LEVEL,3)*MOD(LEVEL,5)))
--------------------------------------------------
                                            234168

- On génère les 1000 premiers entiers
- On les aditionne... avec une astuce :
  - s'ils sont multiples, le reste est 0 et le facteur est neutre
 - s'il ne sont pas multiple,1 - le produit des mods est strictement négatif, et on multiplie par 0... 
 
2) La méthode mathématique

Ben on sait additionner les nombre de 1 à N : N (N+1) / 2
(la piste de démo est : en les couplant deux à deux symétriquement par rapport au milieu, c'est comme multiplier le terme du milieu par 2)
 
Sachant que sommer des multiples de 3 (resp. 5)... c'est comme sommer des entiers consécutifs puis les multiplier par 3 (resp. 5) (vive la distributivité !), il suffit de :
- sommer les multiples de 3
- sommer les multiples de 5
- ah zut il y en a qu'on a ajoutés deux fois... ce sont les multiples de 3 et de 5 : on retranche les multiples de 3 et de 5 (ça vous rappelle pas une formule de patates ?)  
Ce qui donne :
 
SQL> SELECT
  2      trunc (1000/3) * (trunc (1000/3) + 1) / 2 * 3
  3     + trunc (1000/5) * (trunc (1000/5) + 1) / 2 * 5
  4     - trunc (1000/15) * (trunc(1000/15) + 1) /2 * 15 as res
  5  FROM DUAL
  6  /

       RES
----------
    234168

Attendez, je cherche une conclusion à tout cela...
Ho !
 
La mort, n'est qu'une étape de la vie !
EDIT : merci Keeh pour ta vigilance !
Par Pacman - Publié dans : SQL cosmétique
Ecrire un commentaire - Voir les 3 commentaires
Vendredi 29 avril 2011 5 29 /04 /Avr /2011 13:49

La récursivité pure SQL sous Oracle, c'est pas évident avant la 11g.
Souvent lors de petits défis entre amis, je me suis bien pris la tête, sans grands résultats.
... jusqu'aujourd'hui, où j'ai dépilé une importante tâche de mon énooooorme to-do list : me documenter sur la CLAUSE MODEL de 10g.

 

A vrai dire, je ne suis toujours pas sûr de ce qu'on peut en faire dans la vraie vie... mais en tous cas on peut bien rigoler avec !

 

1) La clause MODEL en quelques mots

 

S'appliquant sur le résultat d'un SELECT, elle permet de considérer le résultat comme un tableau (éventuellement multi-dimensionnel), et de faire joujou avec.
 

Pour avoir les détails, je vous renvoie vers l'excellent article de l'excellent Antoun qui est en fait une excellente traduction d'un non moins excellent Neerlandais dont j'ai oublié le très excellent nom (je me souviens juste qu'il fait plein de points au scrabble) :
http://antoun.developpez.com/oracle/model/

 

2) Les suites numériques !

 

Donc voilà moi ce que j'en ai retenu, c'est qu'on peut créer des nouvelles "cases" dans le "tableau", et ce de manière itérative en faisant référence aux cases précédentes.

 

Du coup, mon grand fantasme est réalisé : sortir la sutie de Fibonacci en "pur" SQL :


  SELECT n, u
  FROM (
    SELECT 0 n, 0 U FROM DUAL
    UNION ALL SELECT 1 n, 1 U FROM DUAL
    ) t
  MODEL
   dimension by (n)
   measures (u)
   rules upsert
   iterate(10)
   (u[2+iteration_number] = u[1+iteration_number] + u[iteration_number])

 

Petite explication pour ceux qui ont eu la flemme de suivre le lien :
- Le résultat du SELECT sans le MODEL, c'est le tableau initial.

 

- J'y mets n et U(n), j'y initialise les premiers éléments de la suite : U0 = 0, U1 = 1

 

- Dimension, c'est ce qui indexe le tableau : n, puisqu'on fait du U(n)

 
- Measures, ça doit lister toutes les grandeurs du tableau, ici juste U(n)

 
- Rules : c'est là que commencent les règles de calcul

 


- Upsert : c'est pour dire que si pour un n donné U(n) existe déjà dans le tableau, on le met à jour, sinon on crée une nouvelle ligne. On peut spécifier Update ou Upsert All (ce dernier étant un truc obscur, je le rajoute à ma to-do list)
Iterate(10) : ca dit que la règle qui va suivre, faudra l'appliquer 10 fois (en fait 11, parce qu'on commence à 0 je dirais)

 
- Et la dernière ligne, c'est la règle de calcul à appliquer. Ici c'est la définition de la récurrence : Un+2   =   Un+1   +   Un.

 

Tadaaaam !

 

         N          U
---------- ----------
         0          0
         1          1
         2          1
         3          2
         4          3
         5          5
         6          8
         7         13
         8         21
         9         34
        10         55
        11         89

 

12 rows selected.

 

(Va falloir que je change de froc, je me suis fait dessus)

 

3) Produit cumulatif

 

Je sais pas si ça se fait dans la vraie vie, mais parmi les nombreuses applications de la récursivité, il y a le produit cumulatif.

Et la requête MODEL, je sens qu'elle est encore plus grottesque que la version Exp(Sum(Ln(x))

 

Alors, un jeu de données :

SQL> CREATE TABLE testcumproduct
  2  (grp NUMBER,
  3   ratio NUMBER);

 

C'est parti !

 

SQL> select grp, ratio, rn, nb
  2  from (
  3    select grp, ratio, row_number() over(partition by grp order by null) rn, count(*) over(partition by grp) nb
  4    from testcumproduct
  5  ) t
  6  model
  7    return updated rows
  8    partition by (grp)
  9    dimension by (rn)
 10    measures(ratio, nb)
 11    rules upsert
 12    iterate (100) until (iteration_number = nb[1]-1)
 13    ( ratio[1] = ratio[1] * coalesce(1+ratio[2+iteration_number], 1) );

 

       GRP      RATIO         RN         NB
---------- ---------- ---------- ----------
         1      .3432          1          4
         0     1.0296          1          5

 

A noter que :
- j'ai multiplié par 1+ ratio, pour faire genre ce sont des taux
- La clause partition permet d'émuler le group by
- J'itère en mettant à jour la première ligne de chaque groupe
- La clause return updated rows permet de compléter l'émulation du group by
- J'aurais bien voulu mettre directement mon nb dans en paramètre d'itération, mais il veut pas...

 

Voilà voilà, encore un article complètement inutile, vous êtes bien sur le PacBlog !

 

 

Par Pacman - Publié dans : SQL cosmétique
Ecrire un commentaire - Voir les 0 commentaires
Mardi 9 mars 2010 2 09 /03 /Mars /2010 17:24

Recently, i've read an article from Steven Feuerstein about stripping strings :
http://www.oracle.com/technology/oramag/oracle/06-jan/o16plsql.html

Starting with Oracle 10g, you can use REGEXP_REPLACE to strip any string from a list of given chars.
However, in earlier version, he illustrates the use of the TRANSLATE function for this purpose.

The issues can be summed up as :
- Passing a NULL second argument to the translate function nullyfies the result :

SELECT translate('Steven Feuerstein', 'x', '') FROM dual;
T
-
 
1 row selected.

- The trick : use a joker char which you replace by... itself

SELECT translate('Steven Feuerstein', 'xe', 'x') FROM dual
TRANSLATE('S
------------
Stvn Furstin

1 row selected.

... but you cannot use a constant joker character for all of you strippings, because this character might be the one you want to strip off !
That's why Uncle Steven suggest you should have this joker char beeing as well an argument of your stripping function.

Today in this article, i'll just show a funny way to do it without joker char argument ;)
Disclaimer : i don't want to compete with master Steven, just having a little fun with Oracle SQL !


1) CONNECT BY LEVEL forever !

Let's use the CONNECT BY LEVEL bug to "spell" the string in rows :

SELECT substr('Steven Feuerstein', level, 1) as c, level as rk
FROM dual
CONNECT BY level <= length('Steven Feuerstein')

C         RK
- ----------
S          1
t          2
e          3
v          4
e          5
n          6
           7
F          8
e          9
u         10
e         11
r         12
s         13
t         14
e         15
i         16
n         17

17 rows selected.

Now the stripping just looks like a filter :

SELECT c, row_number() over(order by rk) rk from
    (SELECT substr('Steven Feuerstein', level, 1) as c, level as rk
    FROM dual
    CONNECT by level <= length('Steven Feuerstein')) inp
 WHERE inp.c <> 'e'

C         RK
- ----------
S          1
t          2
v          3
n          4
           5
F          6
u          7
r          8
s          9
t         10
i         11
n         12


12 rows selected.

2) Re-aggregate it... using replace ?!

You sure noticed the ranking of the lines in the previous query. The purspose was obvious : beeing able to re-aggregate the chars after filtering !

In Oracle 11g version, the build in function LISTAGG would make it quite easy.

I choose the XMLAGG method that works in 9i :
- you put your string in single XML tags
- you aggregate the tags...
... then you strip the tags from the resulting string !


Really funny, was there really an issue from the beginning ?


SELECT replace('Steven Feuerstein', 'e', '') as A, translate('Steven Feuerstein', 'e', '') AS b
FROM dual;

REPLACE('STE T
------------ -
Stvn Furstin 


1 row selected.


Yeah, just as expected !
But you can only give one character to strip (unless you want to strip it from a string, which differs from stripping several different chars) as argument of the REPLACE function.


Let's change our example to prove the worthiness of our trick and strip the input string from two chars : "e" and "t"
... and re-aggregate the result :


SELECT replace(replace(xmlagg(xmlelement("marcel", c) order by rk), '<marcel>', ''), '</marcel>', '')
FROM
(SELECT c, row_number() over(order by rk) rk from
    (SELECT substr('Steven Feuerstein', level, 1) as c, level as rk
    FROM dual
    CONNECT by level <= length('Steven Feuerstein')) inp
 WHERE inp.c NOT IN (SELECT substr('et', level, 1) FROM dual CONNECT BY level <= length('et'))
 )

REPLACE(REPLACE(XMLAGG(XMLELEMENT("MARCEL",C)ORDERBYRK),'<MARCEL>',''),'</MARCEL
--------------------------------------------------------------------------------
Svn Fursin                                        
                             

1 row selected.

Great...
...But if you really want to use this underperforming method that could become obsolete whenever the CONNECT BY LEVEL bug would be fixed, you have to put it in a function !
Because you cannot "pass" arguments to the deeper levels of a nested query expression.


Cosmetic SQL Rules !


 

Par Pacman - Publié dans : SQL cosmétique
Ecrire un commentaire - Voir les 0 commentaires

Catégories

Recherche

Calendrier

Mai 2012
L M M J V S D
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
<< < > >>
Créer un blog gratuit sur over-blog.com - Contact - C.G.U. - Rémunération en droits d'auteur - Signaler un abus - Articles les plus commentés