<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Cogito Ergo Dev</title>
  
  <subtitle>Cogito Ergo Dev vous fait découvrir le web et les bases de données relationnelles.</subtitle>
  <link href="https://cogito-ergo-dev.fr/atom.xml" rel="self"/>
  
  <link href="https://cogito-ergo-dev.fr/"/>
  <updated>2023-07-17T08:39:03.992Z</updated>
  <id>https://cogito-ergo-dev.fr/</id>
  
  <author>
    <name>Michel Petit</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Talend et authentification Windows pour accéder à SQL Server</title>
    <link href="https://cogito-ergo-dev.fr/blog/775/talend-et-authentification-windows-pour-acceder-a-sql-server/"/>
    <id>https://cogito-ergo-dev.fr/blog/775/talend-et-authentification-windows-pour-acceder-a-sql-server/</id>
    <published>2023-07-09T17:17:00.000Z</published>
    <updated>2023-07-17T08:39:03.992Z</updated>
    
    <content type="html"><![CDATA[<p>Pouvoir configurer une connexion vers SQL Server 2019 avec Talend 8 en utilisant l’authentification Windows peut relever de la course d’obstacle vues les informations contradictoires ou incomplètes sur le web à ce sujet. J’ai réussi à y parvenir et je vous explique comment faire.</p><span id="more"></span><h2 id="Contexte"><a href="#Contexte" class="headerlink" title="Contexte"></a>Contexte</h2><p>Dans le cadre de mon travail, je devais avoir des jobs Talend (version 8, Java 11) qui devaient se connecter à une base de données SQL Server (version 2019). Jusqu’ici, aucune difficulté majeure. Une connexion classique TCP&#x2F;IP avec login&#x2F;mot de passe SQL.</p><p>Mais un jour, la DSI de cette entreprise changea quelques protocoles, notamment la connexion à la base de données, qui devaient dorénavant s’effectuer via l’authentification Windows uniquement. J’avais déjà vu que cela était possible avec Talend mais je ne l’avais pas encore testé en condition réelle.</p><h2 id="Solution"><a href="#Solution" class="headerlink" title="Solution"></a>Solution</h2><p>La solution passe par plusieurs actions. Mais sur le net, difficile d’avoir une information claire et fonctionnelle. Au bout de plusieurs recherches et essais, j’ai fini par avoir une solution opérationnelle.</p><h3 id="Premiere-action-recuperer-les-drivers-JDBC-sur-le-site-de-Microsoft"><a href="#Premiere-action-recuperer-les-drivers-JDBC-sur-le-site-de-Microsoft" class="headerlink" title="Première action : récupérer les drivers JDBC sur le site de Microsoft"></a>Première action : récupérer les drivers JDBC sur le site de Microsoft</h3><p>Le drivers JDBC, sous la forme d’un fichier ZIP est proposé à cette adresse par Microsoft : <a href="https://learn.microsoft.com/en-us/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-ver15">https://learn.microsoft.com/en-us/sql/connect/jdbc/download-microsoft-jdbc-driver-for-sql-server?view=sql-server-ver15</a></p><p>Cette archive servira à deux choses : </p><ul><li>avoir une DLL nécessaire pour l’authentification,</li><li>avoir le fichier JAR qui sera utilisé par Talend pour se connecter à la DB (plus à jour que Talend&#x2F;Java 11)</li></ul><p>Une fois téléchargée, décompressez-la, peut importe l’endroit.</p><h3 id="Deuxieme-action-determiner-la-version-Java-x86-ou-x64"><a href="#Deuxieme-action-determiner-la-version-Java-x86-ou-x64" class="headerlink" title="Deuxième action : déterminer la version Java (x86 ou x64)"></a>Deuxième action : déterminer la version Java (x86 ou x64)</h3><p>Dans PowerShell, identifiez la version de Java (x86 ou x64) via : </p><pre class="language-powershell" data-language="powershell"><code class="language-powershell">java <span class="token operator">-</span>version</code></pre><p>Attention ! ce n’est pas parce que vous êtes sur un Windows 64 bit que la version java installée est 64 bit ! Vérifiez avant !</p><p>En fonction de cela, dans le dossier <code>auth</code> de l’archive, sélectionnez la bonne DLL en allant dans le bon sous-répertoire en fonction de votre version java (x86 ou x64) vue juste avant.</p><h3 id="Troisieme-action-mise-en-place-de-la-DLL"><a href="#Troisieme-action-mise-en-place-de-la-DLL" class="headerlink" title="Troisième action : mise en place de la DLL"></a>Troisième action : mise en place de la DLL</h3><p>C’est là qu’on a des informations contradictoires sur le net à ce sujet.</p><p>En étant <strong>administrateur Windows</strong>, copiez cette DLL dans le répertoire <code>bin</code> de votre <strong>installation Java</strong> (et non pas dans <code>System32</code> de Windows comme on peut le trouver dans certaines solutions).</p><p>Dans mon cas, j’ai fait ainsi :  </p><pre class="language-powershell" data-language="powershell"><code class="language-powershell"><span class="token function">cp</span> C:\Users\Michel\Downloads\sqljdbc_12<span class="token punctuation">.</span>2<span class="token punctuation">.</span>0<span class="token punctuation">.</span>0_fra\sqljdbc_12<span class="token punctuation">.</span>2\fra\auth\x64\mssql-jdbc_auth-12<span class="token punctuation">.</span>2<span class="token punctuation">.</span>0<span class="token punctuation">.</span>x64<span class="token punctuation">.</span>dll C:\Program Files\Java\jdk-11<span class="token punctuation">.</span>0<span class="token punctuation">.</span>15<span class="token punctuation">.</span>1\bin\</code></pre><p>Adaptez cela en fonction de <strong>votre environnement</strong> et de <strong>l’archive téléchargée</strong>. Ayant pris une archive avec le traduction en français, j’ai un dossier <code>fra</code>, mais pour d’autres langues vous aurez par exemple <code>enu</code> pour Universal English ou encore d’autres possibilités. Ne copiez pas à l’aveugle ma commande !</p><h3 id="Quatrieme-action-utiliser-le-JAR-dans-Talend"><a href="#Quatrieme-action-utiliser-le-JAR-dans-Talend" class="headerlink" title="Quatrième action : utiliser le JAR dans Talend"></a>Quatrième action : utiliser le JAR dans Talend</h3><p>Si vous ne passez pas par cette étape, vous aurez probablement des erreurs Java lors de la compilation de vos jobs.</p><p>Affichez dans Talend l’onglet listant les modules (via Fenête &gt; Montrer la vue).</p><p>Dans la liste des modules Talend, tapez <code>mssql-jdbc.jar</code> dans la zone de recherche afin de rechercher les éléments concernés par ce JAR. </p><p>Ensuite, dans la liste des modules, sélectionnez-en un au hasard, cliquez sur le bouton de modification (en bout de ligne). Dans la fenêtre apparaissant, modifiez le champ URI maven personnalisé par l’URL de MSSQL JDBC que vous trouverez sur Maven. Ici, c’est <a href="https://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc/12.2.0.jre11">https://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc/12.2.0.jre11</a>. Talend modifie l’URL automatiquement afin d’avoir une URI Maven compatible. Validez.</p><p>Si vous devez revenir en arrière, il suffit de refaire les manipulations pour revenir à la popup et vous décocher la case URI MVN personnalisée.</p><h3 id="Cinquieme-action-utiliser-le-bon-driver"><a href="#Cinquieme-action-utiliser-le-bon-driver" class="headerlink" title="Cinquième action : utiliser le bon driver"></a>Cinquième action : utiliser le bon driver</h3><p>Ensuite, dans Talend, pour une connexion DB devant utiliser l’identification intégrée de Windows, il faut sélectionner le fournisseur <strong>JDBC Microsoft</strong> et non plus JTDS Open source, puis il faut laisser les champs login&#x2F;mot de passe vides, mais vous devrez spécifier la chaîne de caractères suivante dans <em>Paramètres supplémentaires JDBC</em> : </p><pre class="language-none"><code class="language-none">&quot;integratedSecurity&#x3D;true;trustServerCertificate&#x3D;true&quot;</code></pre><p>Un test de votre connexion devrait fonctionner et s’achever sans erreur.</p><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>Si vous avez été dans mon cas, à tenter de rendre fonctionnelle une connection vers SQL Server 2019 à l’aide de Talend 8 avec Java 11, et que cela été infructueux, alors j’espère que mon article vous aura servi dans votre cas.</p>]]></content>
    
    
    <summary type="html">Connexion vers SQL Server 2019 avec Talend 8 en utilisant l’authentification Windows</summary>
    
    
    
    
    <category term="sql server" scheme="https://cogito-ergo-dev.fr/etiquettes/sql-server/"/>
    
    <category term="talend" scheme="https://cogito-ergo-dev.fr/etiquettes/talend/"/>
    
    <category term="windows" scheme="https://cogito-ergo-dev.fr/etiquettes/windows/"/>
    
  </entry>
  
  <entry>
    <title>Oracle Database Express Edition avec Docker</title>
    <link href="https://cogito-ergo-dev.fr/blog/10095/oracle-database-express-edition-avec-docker/"/>
    <id>https://cogito-ergo-dev.fr/blog/10095/oracle-database-express-edition-avec-docker/</id>
    <published>2022-04-28T20:30:00.000Z</published>
    <updated>2022-04-28T23:08:09.918Z</updated>
    
    <content type="html"><![CDATA[<p>Vous avez besoin d’utiliser Oracle comme bac-à-sable sur votre machine, pour apprendre, ou expérimenter, sans dépenser de grosses sommes d’argent, alors la solution de facilité est de passer par une image Docker reprenant la base dedonnées Oracle en version Express Edition.</p><span id="more"></span><h2 id="Pourquoi-passer-par-Docker"><a href="#Pourquoi-passer-par-Docker" class="headerlink" title="Pourquoi passer par Docker"></a>Pourquoi passer par Docker</h2><p><strong>Oracle Express Edition</strong> (ou <strong>xe</strong> de son petit nom) est disponible gratuitement, mais son installation simple, sous linux, nécessite d’être sur une distribution compatible <a href="https://www.redhat.com/fr/technologies/linux-platforms/enterprise-linux">RedHat</a> comme <a href="https://www.centos.org/centos-linux/">CentOS</a> ou sans surprise <a href="https://www.oracle.com/fr/linux/">Oracle Linux</a> (OL), la distribution proposée par Oracle. Si vous êtes sous une <strong>autre distribution</strong>, l’installation devient <strong>compliquée</strong>. Ce n’est vraiment pas très fair-play de lapart d’Oracle, quand on compare avec <strong>Microsoft SQL Server Developer</strong> qui peut s’installer très facilement sur<a href="https://docs.microsoft.com/fr-fr/sql/linux/sql-server-linux-setup?view=sql-server-ver15">différentes distributions Linux</a>. </p><p>Bref. </p><p>Si comme moi, vous utilisez une distribution Ubuntu, par chance, une image Docker très pratique permet l’utilisation d’Oracle XE dans un conteneur, et en plus, la prise en main est aisée.</p><h2 id="Installation"><a href="#Installation" class="headerlink" title="Installation"></a>Installation</h2><p>Pour ce qui suit, je me base sur une <strong>Ubuntu 20.04 LTS</strong> et sur <strong>Oracle Database XE 21c</strong>.</p><p>Je passe les détails sur l’installation de Docker sur votre système, <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04-fr">un article de Digital Ocean l’explique très bien</a>.</p><p>L’image Docker qui nous intéresse ici est celle de <code>gvenzl/oracle-xe</code>, disponible sur <a href="https://hub.docker.com/r/gvenzl/oracle-xe">Docker Hub</a>.</p><p>Récupérez cette image ainsi :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> pull gvenzl/oracle-xe</code></pre><p>Vous aurez alors la dernière version disponible. Au moment d’écrire ces lignes, il s’agissait d’Oracle Database <strong>21c</strong>. Si vous souhaitez installer une autre version, utilisez le <strong>système des tags</strong> de Docker. Par exemple, pour avoir la version <strong>11g2</strong>, faites ainsi :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> pull gvenzl/oracle-xe:11.2.0.2</code></pre><h2 id="Lancement"><a href="#Lancement" class="headerlink" title="Lancement"></a>Lancement</h2><p>Maintenant que nous avons l’image, préparons un peu le terrain pour avoir une persistance des données : ce serait bien de pouvoir les conserver d’une foissur l’autre non ? </p><p>Qui dit persistance, dit volume : pour cela, créons un volume Docker et appelons-le <code>volora</code> en tapant la commande suivante :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> volume create volora</code></pre><p>Vous avez maintenant un volume pour garder vos modifications en base de données.</p><p>Définissons maintenant un mot de passe pour les utilisateurs <code>SYS</code> et <code>SYSTEM</code> ainsi que pour un utilisateur normal, arbitrairement dénommé <code>TEST</code> :</p><ul><li>Pour <code>SYS</code> et <code>SYSTEM</code>, pour l’exemple, choisissons <code>oracle</code> comme mot de passe.</li><li>Pour l’utilisateur normal, son nom sera <code>TEST</code> et son mot de passe sera <code>elcaro</code>.</li></ul><p>Il va de soi qu’il vous faudra choisir des mots de passe plus robustes.</p><p>Nous avons l’image, les mots de passe, le volume, il n’y a plus qu’à mettre tout ça ensemble ! Lançons Docker ainsi, en nommant notre container <code>oracle_xe</code> :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run -d -p <span class="token number">1521</span>:1521 <span class="token punctuation">\</span>    --name<span class="token operator">=</span>oracle_xe <span class="token punctuation">\</span>    -e <span class="token assign-left variable">ORACLE_PASSWORD</span><span class="token operator">=</span>oracle <span class="token punctuation">\</span>    -e <span class="token assign-left variable">APP_USER</span><span class="token operator">=</span>TEST <span class="token punctuation">\</span>    -e <span class="token assign-left variable">APP_USER_PASSWORD</span><span class="token operator">=</span>elcaro <span class="token punctuation">\</span>    -v volora:/opt/oracle/oradata <span class="token punctuation">\</span>    gvenzl/oracle-xe</code></pre><p>Si vous avez un problème de droit, utilisez <code>sudo</code> devant la commande.</p><p>Notez bien le chemin associé au volume : il correspond à celui des versions d’Oracle <strong>postérieure à 11g2</strong>. Si vous avez spécifié un tag pour installer l’image Docker pour Oracle XE 11g2, alors la commande sera la suivante :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> run -d -p <span class="token number">1521</span>:1521 <span class="token punctuation">\</span>    --name<span class="token operator">=</span>oracle_xe <span class="token punctuation">\</span>    -e <span class="token assign-left variable">ORACLE_PASSWORD</span><span class="token operator">=</span>oracle <span class="token punctuation">\</span>    -e <span class="token assign-left variable">APP_USER</span><span class="token operator">=</span>TEST <span class="token punctuation">\</span>    -e <span class="token assign-left variable">APP_USER_PASSWORD</span><span class="token operator">=</span>elcaro <span class="token punctuation">\</span>    -v volora:/u01/app/oracle/oradata <span class="token punctuation">\</span>    gvenzl/oracle-xe</code></pre><p>Le chemin change de <code>/opt/oracle/oradata</code> à <code>/u01/app/oracle/oradata</code>.</p><p>Par défaut, le <code>service name</code> est défini à <code>XEPDB1</code>.</p><h2 id="Utilisation"><a href="#Utilisation" class="headerlink" title="Utilisation"></a>Utilisation</h2><p>Testons maintenant la connexion à la base avec <a href="https://www.oracle.com/fr/database/technologies/appdev/sqlcl.html">SQLcl</a> et avec <a href="https://dbeaver.io/">DBeaver</a>.</p><h3 id="SQLcl"><a href="#SQLcl" class="headerlink" title="SQLcl"></a>SQLcl</h3><p>Testons la connexion avec l’utilisateur <code>TEST</code> :</p><pre class="language-bash" data-language="bash"><code class="language-bash">sqlcl TEST/elcaro@localhost:1521/XEPDB1</code></pre><p>On se connecte bien comme le montre la capture suivante :</p>    <picture>        <source srcset="./connexion-utilisateur-test.webp" type="image/webp">        <img            src="./connexion-utilisateur-test.png" type="image/png"            alt="Première ouverture de session pour l’utilisateur TEST"  class="uk-margin-auto uk-display-block" uk-img        >    </picture>    <p>Testons la création d’une table :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> THING <span class="token punctuation">(</span>NAME VARCHAR2 <span class="token punctuation">(</span> <span class="token number">100</span> <span class="token punctuation">)</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">,</span>CREATED_AT <span class="token keyword">DATE</span> <span class="token keyword">DEFAULT</span> SYSDATE <span class="token operator">NOT</span> <span class="token boolean">NULL</span>  <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Et insérons-y une donnée :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> THING <span class="token punctuation">(</span>NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token string">'Foo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Tout se déroule bien comme le montre la capture suivante :</p>    <picture>        <source srcset="./utilisateur-test-create-table.webp" type="image/webp">        <img            src="./utilisateur-test-create-table.png" type="image/png"            alt="L’utilisateur TEST crée une table sous Oracle avec SQLcl"  class="uk-margin-auto uk-display-block" uk-img        >    </picture>    <p>Une sélection de la table le confirme :</p>    <picture>        <source srcset="./utilisateur-test-select-table.webp" type="image/webp">        <img            src="./utilisateur-test-select-table.png" type="image/png"            alt="L’utilisateur TEST sélectionne le contenu de la nouvelle table sous Oracle avec SQLcl"  class="uk-margin-auto uk-display-block" uk-img        >    </picture>    <p>Bien.</p><p>Maintenant, arrêtons notre conteneur Docker, et relançons-le, pour vérifier la persistance des données :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">docker</span> stop oracle_xe<span class="token function">docker</span> start oracle_xe</code></pre><p>En se reconnectant et en effectuant le <code>SELECT</code> à nouveau, on a bien la table qui est toujours présente et avec du contenu :</p>    <picture>        <source srcset="./utilisateur-test-select-encore-table.webp" type="image/webp">        <img            src="./utilisateur-test-select-encore-table.png" type="image/png"            alt="L’utilisateur TEST sélectionne le contenu de la nouvelle table sous Oracle avec SQLcl après redémarrage du conteneur"  class="uk-margin-auto uk-display-block" uk-img        >    </picture>    <p>Il reste un petit test à faire : quid de l’utilisateur <code>SYSTEM</code> ? Peut-il bien se connecter ? Réponse :</p>    <picture>        <source srcset="./connexion-utilisateur-system.webp" type="image/webp">        <img            src="./connexion-utilisateur-system.png" type="image/png"            alt="L’utilisateur SYSTEM tente sa première connexion sous Oracle avec SQLcl"  class="uk-margin-auto uk-display-block" uk-img        >    </picture>    <p>Verdict : tout fonctionne.</p><h3 id="DBeaver"><a href="#DBeaver" class="headerlink" title="DBeaver"></a>DBeaver</h3><p>Pour ajouter la connexion à DBeaver, assurer vous d’avoir les données au bon endroit, comme sur la capture suivante :</p>    <picture>        <source srcset="./configuration-connexion-dbeaver.webp" type="image/webp">        <img            src="./configuration-connexion-dbeaver.png" type="image/png"            alt="Écran de configuration DBeaverde la connexion sur Oracle Database avec l’utilisateur TEST"  class="uk-margin-auto uk-display-block" uk-img        >    </picture>    <p>Une fois la connexion créée, vous aurez remarqué que ce qui a été fait avant est bel et bien présent.</p>    <picture>        <source srcset="./connexion-dbeaver.webp" type="image/webp">        <img            src="./connexion-dbeaver.png" type="image/png"            alt="Écran de requête sur Oracle Database avec l’utilisateur TEST sous DBeaver"  class="uk-margin-auto uk-display-block" uk-img        >    </picture>    <h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>Si vous n’êtes pas sur une distribution linux <em>RHEL like</em>, alors l’utilisation d’un <strong>conteneur Docker</strong> est une franche bonne idée pour travailler avec <strong>Oracle Database XE</strong>. Facile et rapide, c’est l’idéal.</p>]]></content>
    
    
    <summary type="html">Apprivoisez Oracle Database XE sous linux Ubuntu à l’aide de Docker.</summary>
    
    
    
    
    <category term="db" scheme="https://cogito-ergo-dev.fr/etiquettes/db/"/>
    
    <category term="sql" scheme="https://cogito-ergo-dev.fr/etiquettes/sql/"/>
    
    <category term="oracle" scheme="https://cogito-ergo-dev.fr/etiquettes/oracle/"/>
    
    <category term="docker" scheme="https://cogito-ergo-dev.fr/etiquettes/docker/"/>
    
  </entry>
  
  <entry>
    <title>Trois pièges de débutant sous Oracle</title>
    <link href="https://cogito-ergo-dev.fr/blog/1641/trois-pieges-de-debutant-sous-oracle/"/>
    <id>https://cogito-ergo-dev.fr/blog/1641/trois-pieges-de-debutant-sous-oracle/</id>
    <published>2022-04-23T10:35:19.000Z</published>
    <updated>2022-04-23T22:11:03.416Z</updated>
    
    <content type="html"><![CDATA[<p>Si vous avez déjà fait du SQL et que vous mettez les pieds dans une base dedonnées Oracle, vous risquez d’être confronté⋅e rapidement à 3 pièges danslesquels tombent beaucoup de débutant⋅e⋅s.</p><span id="more"></span><h2 id="Premier-piege-les-SELECT-sans-FROM-en-erreur"><a href="#Premier-piege-les-SELECT-sans-FROM-en-erreur" class="headerlink" title="Premier piège : les SELECT sans FROM en erreur"></a>Premier piège : les SELECT sans FROM en erreur</h2><p>Vous commencez votre premier jour d’utilisation sur Oracle. vous souhaitez juste lancer un test basique, comme par exemple faire un calcul ou tester une fonctionde chaîne.</p><p>Vous faites alors :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token number">2</span> <span class="token operator">+</span> <span class="token number">3</span><span class="token punctuation">;</span></code></pre><p>Ou encore :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> UPPER<span class="token punctuation">(</span><span class="token string">'coucou'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Et à chaque fois, pour ces requêtes très simples, vous obtenez une erreur :</p><pre class="language-none"><code class="language-none">ORA-00923: mot-clé FROM absent à l&#39;emplacement prévu</code></pre><p>Notez bien le début du message d’erreur : ils commencent tous ainsi, avec <code>ORA</code>suivi d’un numéro à 5 chiffres pour le code d’erreur.</p><p>Le message nous dit que le mot-clé <code>FROM</code> est manquant, pourtant, les requêtesque vous faites semblent correctes.</p><p>Cela va vous surprendre, mais sous Oracle, il faut <strong>toujours</strong> utiliser le mot-clé<code>SELECT</code> avec le mot-clé <code>FROM</code>, sinon, vous obtenez l’erreur mentionnée ci-dessus.</p><p>Mais alors, comment faire pour ce genre de requête ? La réponse estl’utilisation d’une <strong>table fictive</strong> appelée <code>dual</code>. Dès que vous avez des requêtes ne faisant pas intervenir de table, de vue ou de CTE, alors ajoutezsystématiquement <code>FROM dual</code> à votre requête.</p><p>Nous pouvons donc réécrire les requêtes précédentes ainsi :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token number">2</span> <span class="token operator">+</span> <span class="token number">3</span> <span class="token keyword">FROM</span> dual<span class="token punctuation">;</span></code></pre><p>Elle donne bien le bon résultat et sans erreur :</p><pre class="language-none"><code class="language-none">2+3|---+  5|</code></pre><p>Et maintenant avec :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> UPPER<span class="token punctuation">(</span><span class="token string">'coucou'</span><span class="token punctuation">)</span> <span class="token keyword">FROM</span> dual<span class="token punctuation">;</span></code></pre><p>Elle fonctionne également :</p><pre class="language-none"><code class="language-none">UPPER(&#39;COUCOU&#39;)|---------------+COUCOU         |</code></pre><p>Voyons maintenant un autre cas : une CTE récursive.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> cte_exemple<span class="token punctuation">(</span>n<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token number">0</span><span class="token keyword">UNION</span> <span class="token keyword">ALL</span><span class="token keyword">SELECT</span> n <span class="token operator">+</span> <span class="token number">1</span> <span class="token keyword">FROM</span> cte_exemple <span class="token keyword">WHERE</span>  n <span class="token operator">&lt;</span> <span class="token number">9</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> cte_exemple<span class="token punctuation">;</span></code></pre><p>Vous obtiendrez encore l’erreur du mot-clé <code>FROM</code> manquant.</p><p>Donc pour lancer une CTE récursive, il faut penser à spécifier le <code>FROM dual</code> !</p><p>En voici la version corrigée :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> cte_exemple<span class="token punctuation">(</span>n<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token number">0</span> <span class="token keyword">FROM</span> dual<span class="token keyword">UNION</span> <span class="token keyword">ALL</span><span class="token keyword">SELECT</span> n <span class="token operator">+</span> <span class="token number">1</span> <span class="token keyword">FROM</span> cte_exemple <span class="token keyword">WHERE</span>  n <span class="token operator">&lt;</span> <span class="token number">9</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> cte_exemple<span class="token punctuation">;</span></code></pre><p>Et cela donne le résultat escompté :</p><pre class="language-none"><code class="language-none">N|-+0|1|2|3|4|5|6|7|8|9|</code></pre><p>Donc retenez bien : pas de <code>SELECT</code> sans <code>FROM</code> !</p><h2 id="Deuxieme-piege-CREATE-TABLE-DEFAULT-en-erreur"><a href="#Deuxieme-piege-CREATE-TABLE-DEFAULT-en-erreur" class="headerlink" title="Deuxième piège : CREATE TABLE DEFAULT en erreur"></a>Deuxième piège : CREATE TABLE DEFAULT en erreur</h2><p>Vous voulez créer une table assez simple, avec certaines colonnes devant avoirune valeur par défaut, et ne devant pas valoir <code>NULL</code>.</p><p>Cela pourrait être l’exemple suivant :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> THING <span class="token punctuation">(</span>NAME VARCHAR2 <span class="token punctuation">(</span> <span class="token number">100</span> <span class="token punctuation">)</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">,</span>CREATED_AT <span class="token keyword">DATE</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">DEFAULT</span> SYSDATE  <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Vous exécutez la requête de création de table, sûr⋅e de vous, et là vous obtenezl’étrange erreur suivante :</p><pre class="language-none"><code class="language-none">ORA-00907: parenthèse de droite absente</code></pre><p>Pourtant, il ne manque pas de parenthèse. Alors, pourquoi cette erreur est-ellelevée ?</p><p>L’explication est qu’Oracle est <strong>peu tolérant au changement d’ordre dans les déclarations</strong>. Pourune syntaxe qui serait valable avec un autre SGBD, on obtient une erreur avec Oracle, mais une erreur pas claire car comme il s’attend à un ordre différent des éléments, il soulève une erreur par rapport à quelque chose qu’il attend et qui n’a pas lieu d’être ici. Pour faire simple, <strong>il soulève une erreur innapropriée</strong>.</p><p>La solution est de faire attention à l’emplacement de la clause <code>DEFAULT</code>. Il faut ici la placer après le nom de la colonne, comme ceci par exemple :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> THING <span class="token punctuation">(</span>NAME VARCHAR2 <span class="token punctuation">(</span> <span class="token number">100</span> <span class="token punctuation">)</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">,</span>CREATED_AT <span class="token keyword">DATE</span> <span class="token keyword">DEFAULT</span> SYSDATE <span class="token operator">NOT</span> <span class="token boolean">NULL</span>  <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>La requête s’exécute sans erreur, la table est bien créée.</p><p>Insérons-y une ligne :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> THING <span class="token punctuation">(</span>NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token string">'Foo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Voyons si la valeur par défaut a bien été prise :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> THING<span class="token punctuation">;</span></code></pre><p>La sélection donne :</p><pre class="language-text" data-language="text"><code class="language-text">NAME|CREATED_AT             |----+-----------------------+Foo |2022-04-23 11:41:19.000|</code></pre><p>Nous avons bien la valeur par défaut pour la date de création.</p><p>Donc retenez bien : attention <strong>à l’ordre de déclaration</strong> qui est assez rigide chez Oracle, contrairement à d’autres SGBD.</p><h2 id="Troisieme-piege-les-cles-primaires-auto-incrementees"><a href="#Troisieme-piege-les-cles-primaires-auto-incrementees" class="headerlink" title="Troisième piège : les clés primaires auto-incrémentées"></a>Troisième piège : les clés primaires auto-incrémentées</h2><p>Oracle ne propose pas une, pas deux, mais trois façons différentes de déclarerune clé primaire auto-incrémentée. Selon ce que vous aurez choisi, vousobtiendrez ou non des erreurs lors des insertions de données.</p><p>Voyons cela avec 3 exemples.</p><h3 id="Auto-incrementation-basique"><a href="#Auto-incrementation-basique" class="headerlink" title="Auto-incrémentation basique"></a>Auto-incrémentation basique</h3><p>Testons directement avec cette déclaration :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> BAR <span class="token punctuation">(</span>ID NUMBER GENERATED <span class="token keyword">AS</span> <span class="token keyword">IDENTITY</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span>NAME VARCHAR2 <span class="token punctuation">(</span> <span class="token number">100</span> <span class="token punctuation">)</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span>  <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>La table est bien créée. insérons maintenant des données :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> BAR <span class="token punctuation">(</span>NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token string">'Foo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Fonctionne et donne ceci :</p><pre class="language-text" data-language="text"><code class="language-text">ID|NAME|--+----+ 1|Foo |</code></pre><p>Insérons en spécifiant une valeur pour la clé primaire :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> BAR <span class="token punctuation">(</span>ID<span class="token punctuation">,</span> NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">'Thing'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Oracle lève l’erreur suivante :</p><pre class="language-text" data-language="text"><code class="language-text">ORA-32795:  impossible d'insérer la valeur dans une colonne d'identité avec            les mots-clés GENERATED ALWAYS</code></pre><p>Voyons avec la valeur <code>NULL</code> pour la clé :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> BAR <span class="token punctuation">(</span>ID<span class="token punctuation">,</span> NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token boolean">NULL</span><span class="token punctuation">,</span> <span class="token string">'Bar'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Oracle lève la même erreur :</p><pre class="language-text" data-language="text"><code class="language-text">ORA-32795:  impossible d'insérer la valeur dans une colonne d'identité avec            les mots-clés GENERATED ALWAYS</code></pre><p>Donc, pour un <code>GENERATED AS IDENTITY</code> il ne faut pas spécifier de valeur pour lechamp de la clé primaire lors des insertions, Oracle se charge de tout.</p><h3 id="Auto-incrementation-par-defaut"><a href="#Auto-incrementation-par-defaut" class="headerlink" title="Auto-incrémentation par défaut"></a>Auto-incrémentation par défaut</h3><p>Passons à la deuxième façon de délarer une clé primaire avec auto-incrémentation.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> BAR2 <span class="token punctuation">(</span>ID NUMBER GENERATED <span class="token keyword">BY</span> <span class="token keyword">DEFAULT</span> <span class="token keyword">AS</span> <span class="token keyword">IDENTITY</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span>NAME VARCHAR2 <span class="token punctuation">(</span> <span class="token number">100</span> <span class="token punctuation">)</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span>  <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>La table est créée, réalisons maintenant différentes insertions :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> BAR2 <span class="token punctuation">(</span>NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token string">'Foo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Cette première insertion sans spécifier de valeur pour la colonneauto-incrémenté passe sans problème.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> BAR2 <span class="token punctuation">(</span>ID<span class="token punctuation">,</span> NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">'Thing'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>La deuxième insertion, en donnant une valeur pour la colonne auto-incrémentée,passe également sans difficulté.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> BAR2 <span class="token punctuation">(</span>ID<span class="token punctuation">,</span> NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token boolean">NULL</span><span class="token punctuation">,</span> <span class="token string">'Bar'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Cette troisième insertion, en passant <code>NULL</code> pour la colonne auto-incrémentée,lève l’erreur suivante :</p><pre class="language-text" data-language="text"><code class="language-text">ERREUR : ORA-01400: impossible d'insérer NULL dans ("TEST"."BAR2"."ID")</code></pre><p>Nous avons donc au final pour cette table :</p><pre class="language-text" data-language="text"><code class="language-text">ID|NAME |--+-----+ 1|Foo  | 2|Thing|</code></pre><p>Donc, pour un <code>GENERATED BY DEFAULT AS IDENTITY</code> il ne faut pas passer la valeur<code>NULL</code> pour le champ de la clé primaire lors des insertions.</p><h3 id="Auto-incrementation-par-defaut-pour-NULL"><a href="#Auto-incrementation-par-defaut-pour-NULL" class="headerlink" title="Auto-incrémentation par défaut pour NULL"></a>Auto-incrémentation par défaut pour NULL</h3><p>Voyons maintenant ce qu’il en est avec la troisième façon de définir un champauto-incrémenté comme clé primaire.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> BAR3 <span class="token punctuation">(</span>ID NUMBER GENERATED <span class="token keyword">BY</span> <span class="token keyword">DEFAULT</span> <span class="token keyword">ON</span> <span class="token boolean">NULL</span> <span class="token keyword">AS</span> <span class="token keyword">IDENTITY</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span>NAME VARCHAR2 <span class="token punctuation">(</span> <span class="token number">100</span> <span class="token punctuation">)</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span>  <span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>La table étant créée, testons les différentes façons d’insérer les données :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> BAR3 <span class="token punctuation">(</span>NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token string">'Foo'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Cette première insertion sans spécifier de valeur pour la colonneauto-incrémentée passe sans problème.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> BAR3 <span class="token punctuation">(</span>ID<span class="token punctuation">,</span> NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">'Thing'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>La deuxième insertion, en donnant une valeur pour la colonne auto-incrémentée,passe également sans difficulté.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> BAR3 <span class="token punctuation">(</span>ID<span class="token punctuation">,</span> NAME<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token boolean">NULL</span><span class="token punctuation">,</span> <span class="token string">'Bar'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>La troisième insertion, en forçant à <code>NULL</code> pour la colonne auto-incrémentée,passe cette fois-ci sans difficulté.</p><p>Voyons maintenant les valeurs insérées :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> BAR3<span class="token punctuation">;</span></code></pre><p>Nous avons bien un <code>ID</code> à 3 pour l’insertion avec <code>NULL</code> :</p><pre class="language-text" data-language="text"><code class="language-text">ID|NAME |--+-----+ 1|Foo  | 2|Thing| 3|Bar  |</code></pre><p>Donc, pour un <code>GENERATED BY DEFAULT ON NULL AS IDENTITY</code>, il est possibled’utiliser tous les types d’insertion possible !</p><h3 id="PK-AI-recapitulatif"><a href="#PK-AI-recapitulatif" class="headerlink" title="PK AI récapitulatif"></a>PK AI récapitulatif</h3><p>Voici un tableau récapitulant les insertions possibles en fonction du type d’auto-incrémentation utilisé.</p><table><thead><tr><th>Déclaration</th><th>insert sans ID PK</th><th>insert ID PK</th><th>insert ID PK NULL</th></tr></thead><tbody><tr><td><code>GENERATED AS IDENTITY</code></td><td><strong>OK</strong></td><td>ERR</td><td>ERR</td></tr><tr><td><code>GENERATED BY DEFAULT AS IDENTITY</code></td><td><strong>OK</strong></td><td><strong>OK</strong></td><td>ERR</td></tr><tr><td><code>GENERATED BY DEFAULT ON NULL AS IDENTITY</code></td><td><strong>OK</strong></td><td><strong>OK</strong></td><td><strong>OK</strong></td></tr></tbody></table><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>J’espère vous avoir fait épargner du temps en recherche de résolution de problèmepour ces quelques particularités d’Oracle qui peuvent surprendre de prime abord.</p>]]></content>
    
    
    <summary type="html">Trois pièges déroutants pour le débutant sous Oracle.</summary>
    
    
    
    
    <category term="db" scheme="https://cogito-ergo-dev.fr/etiquettes/db/"/>
    
    <category term="astuce" scheme="https://cogito-ergo-dev.fr/etiquettes/astuce/"/>
    
    <category term="sql" scheme="https://cogito-ergo-dev.fr/etiquettes/sql/"/>
    
    <category term="oracle" scheme="https://cogito-ergo-dev.fr/etiquettes/oracle/"/>
    
  </entry>
  
  <entry>
    <title>Date de Pâques et nombre de jours dans un mois en PHP</title>
    <link href="https://cogito-ergo-dev.fr/blog/31177/date-de-paques-et-nombre-de-jours-dans-un-mois-en-php/"/>
    <id>https://cogito-ergo-dev.fr/blog/31177/date-de-paques-et-nombre-de-jours-dans-un-mois-en-php/</id>
    <published>2022-04-20T16:19:05.000Z</published>
    <updated>2022-04-20T21:00:21.590Z</updated>
    
    <content type="html"><![CDATA[<p>Certaines fonctions de PHP permettent d’obtenir des informations ayant traitau calendrier. Mais, comment les obtenir si vous n’avez pas l’extension calendar disponible ?</p><span id="more"></span><p>L’extension <a href="https://www.php.net/manual/en/book.calendar.php">Calendar</a> de PHP est très pratique, mais la majeure partie de ses fonctionnalités est très peu utilisée dans l’ensemble.</p><p>Chez nous, en France et dans les pays francophones, les fonctions fréquemment utilisées de cette extension sont <a href="https://www.php.net/manual/fr/function.easter-date">easter_date()</a> et <a href="https://www.php.net/manual/fr/function.cal-days-in-month">cal_days_in_month()</a>.</p><p>Comment faire pour avoir un équivalent si vous ne pouvez pas utiliser l’extension Calendar ? C’est ce que je vais vous montrer.</p><h2 id="Exemple-connu-Paques"><a href="#Exemple-connu-Paques" class="headerlink" title="Exemple connu : Pâques"></a>Exemple connu : Pâques</h2><p>La fonction <code>easter_date()</code> permet d’avoir <strong>la date de Pâques pour une année donnée</strong>.</p><p>Cela est très pratique, car outre Pâques, cela permet d’avoir par déduction lesdates d’autres fêtes comme l’Ascension ou la Pentecôte. Pouvoir déterminer la date de Pâques permet de <strong>déterminer tous les jours fériés ayant un date variable en France</strong>.</p><p>J’ai fait un <a href="/blog/13235/paques-et-autres-conges-avec-mysql/" title="article dernièrement avec MySQL à ce sujet">article dernièrement avec MySQL à ce sujet</a>. Maintenant, transposons le code en PHP. Le but est de créer une fonction prenantun paramètre <code>int</code> pour l’année dont on veut déterminer la date de Pâques, et quisort un object <code>DateTime</code> pour manipuler la date comme on le désire.</p><pre class="language-php" data-language="php"><code class="language-php"><span class="token comment">// https://de.wikipedia.org/wiki/Gau%C3%9Fsche_Osterformel#Eine_erg.C3.A4nzte_Osterformel</span><span class="token keyword">function</span> <span class="token function-definition function">easter</span><span class="token punctuation">(</span><span class="token keyword type-hint">int</span> <span class="token variable">$year</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token class-name class-name-fully-qualified return-type"><span class="token punctuation">\</span>DateTime</span><span class="token punctuation">&#123;</span>    <span class="token variable">$k</span> <span class="token operator">=</span> <span class="token function">intdiv</span><span class="token punctuation">(</span><span class="token variable">$year</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token variable">$m</span> <span class="token operator">=</span> <span class="token number">15</span> <span class="token operator">+</span> <span class="token function">intdiv</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">3</span> <span class="token operator">*</span> <span class="token variable">$k</span> <span class="token operator">+</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token function">intdiv</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">8</span> <span class="token operator">*</span> <span class="token variable">$k</span> <span class="token operator">+</span> <span class="token number">13</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">25</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token variable">$s</span> <span class="token operator">=</span> <span class="token number">2</span> <span class="token operator">-</span> <span class="token function">intdiv</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">3</span> <span class="token operator">*</span> <span class="token variable">$k</span> <span class="token operator">+</span> <span class="token number">3</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token variable">$a</span> <span class="token operator">=</span> <span class="token variable">$year</span> <span class="token operator">%</span> <span class="token number">19</span><span class="token punctuation">;</span>    <span class="token variable">$d</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">19</span> <span class="token operator">*</span> <span class="token variable">$a</span> <span class="token operator">+</span> <span class="token variable">$m</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">30</span><span class="token punctuation">;</span>    <span class="token variable">$r</span> <span class="token operator">=</span> <span class="token function">intdiv</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token variable">$d</span> <span class="token operator">+</span> <span class="token function">intdiv</span><span class="token punctuation">(</span><span class="token variable">$a</span><span class="token punctuation">,</span> <span class="token number">11</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">29</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token variable">$og</span> <span class="token operator">=</span> <span class="token number">21</span> <span class="token operator">+</span> <span class="token variable">$d</span> <span class="token operator">-</span> <span class="token variable">$r</span> <span class="token punctuation">;</span>    <span class="token variable">$sz</span> <span class="token operator">=</span> <span class="token number">7</span> <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token variable">$year</span> <span class="token operator">+</span> <span class="token function">intdiv</span><span class="token punctuation">(</span><span class="token variable">$year</span><span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token variable">$s</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">7</span><span class="token punctuation">;</span>    <span class="token variable">$oe</span> <span class="token operator">=</span> <span class="token number">7</span> <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token variable">$og</span> <span class="token operator">-</span> <span class="token variable">$sz</span><span class="token punctuation">)</span> <span class="token operator">%</span> <span class="token number">7</span><span class="token punctuation">;</span>    <span class="token variable">$os</span> <span class="token operator">=</span> <span class="token variable">$og</span> <span class="token operator">+</span> <span class="token variable">$oe</span><span class="token punctuation">;</span>    <span class="token variable">$result</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>DateTime</span><span class="token punctuation">(</span><span class="token function">sprintf</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'%04s-03-01'</span><span class="token punctuation">,</span> <span class="token variable">$year</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token variable">$result</span><span class="token operator">-></span><span class="token function">add</span><span class="token punctuation">(</span>        <span class="token class-name class-name-fully-qualified static-context"><span class="token punctuation">\</span>DateInterval</span><span class="token operator">::</span><span class="token function">createFromDateString</span><span class="token punctuation">(</span>            <span class="token function">sprintf</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'%s days'</span><span class="token punctuation">,</span> <span class="token variable">$os</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span>        <span class="token punctuation">)</span>    <span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">return</span> <span class="token variable">$result</span><span class="token punctuation">;</span> <span class="token punctuation">&#125;</span></code></pre><p>Il y a d’autres implémentations avec d’autres algorithmes que vous trouverezfacilement sur le net.</p><p>Jouons un peu avec notre fonction :</p><pre class="language-php" data-language="php"><code class="language-php"><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">easter</span><span class="token punctuation">(</span><span class="token number">2022</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'d/m/Y'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// string(10) "17/04/2022"</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">easter</span><span class="token punctuation">(</span><span class="token number">2032</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'d/m/Y'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// string(10) "28/03/2032"</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">easter</span><span class="token punctuation">(</span><span class="token number">2042</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'d/m/Y'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// string(10) "06/04/2042"</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">easter</span><span class="token punctuation">(</span><span class="token number">1789</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'d/m/Y'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">//string(10) "12/04/1789"</span></code></pre><p>Voilà notre première fonction de remplacement opérationnelle ! Vous pouvez comparer à la <a href="https://www.census.gov/data/software/x13as/genhol/easter-dates.html">liste des dates de Pâques</a>.</p><h2 id="Nombre-de-jours-dans-un-mois-donne"><a href="#Nombre-de-jours-dans-un-mois-donne" class="headerlink" title="Nombre de jours dans un mois donné"></a>Nombre de jours dans un mois donné</h2><p>La fonction <code>cal_days_in_month()</code> de l’extension Calendar permet de <strong>récupérerle nombre de jours dans un mois d’une année donnée</strong>. Il est possible despécifier un type de calendrier particulier en prime.</p><p>Si vous ne disposez pas de l’extension Calendar, alors sachez que l’utilisationdes fonctionnalités des dates en natif suffisent.</p><p>Sur un objet <code>DateTime</code>, l’appel à la méthode <code>format()</code> avec l’argument <code>&#39;t&#39;</code>permet de retourner le <strong>nombre de jours dans le mois</strong> de la date pré-définie.</p><p>Par exemple, pour février 2022, on peut faire ceci :</p><pre class="language-php" data-language="php"><code class="language-php"><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">DateTime</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'2022-02'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'t'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// string(2) "28"</span></code></pre><p>L’utilisation n’est pas très pratique, nous allons donc créer une fonction quiprend obligatoirement le mois, et en option, l’année. Si l’année n’est pasfournie, alors c’est l’année en cours qui est prise.</p><p>Et tant qu’à faire, on va retourner une valeur entière <code>int</code>, et non une chaîne decaractères <code>string</code>.</p><pre class="language-php" data-language="php"><code class="language-php"><span class="token keyword">function</span> <span class="token function-definition function">daysInMonth</span><span class="token punctuation">(</span><span class="token keyword type-hint">int</span> <span class="token variable">$month</span><span class="token punctuation">,</span> <span class="token keyword type-hint">int</span> <span class="token variable">$year</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword return-type">int</span><span class="token punctuation">&#123;</span>    <span class="token variable">$date</span> <span class="token operator">=</span> <span class="token function">sprintf</span><span class="token punctuation">(</span>        <span class="token string single-quoted-string">'%s-%02s-01'</span><span class="token punctuation">,</span>         <span class="token function">is_null</span><span class="token punctuation">(</span><span class="token variable">$year</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>DateTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Y'</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token variable">$year</span><span class="token punctuation">,</span>        <span class="token variable">$month</span>    <span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword type-casting">int</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">DateTime</span><span class="token punctuation">(</span><span class="token variable">$date</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'t'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span></code></pre><p>Jouons un peu avec cette fonction :</p><pre class="language-php" data-language="php"><code class="language-php"><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(28)</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2022</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(28)</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2020</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(29)</span></code></pre><p>C’est pas mal, maintenant protégeons-là un peu pour éviter d’entrer des valeursimpossibles comme un mois 13 ou une <a href="https://www.php.net/manual/fr/datetime.construct.php#95610">année hors de l’intervalle -9999&#x2F;9999</a> :</p><pre class="language-php" data-language="php"><code class="language-php"><span class="token keyword">function</span> <span class="token function-definition function">daysInMonth</span><span class="token punctuation">(</span><span class="token keyword type-hint">int</span> <span class="token variable">$month</span><span class="token punctuation">,</span> <span class="token keyword type-hint">int</span> <span class="token variable">$year</span> <span class="token operator">=</span> <span class="token constant">null</span><span class="token punctuation">)</span><span class="token punctuation">:</span> <span class="token keyword return-type">int</span><span class="token punctuation">&#123;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token variable">$month</span> <span class="token operator">></span> <span class="token number">12</span> <span class="token operator">||</span> <span class="token variable">$month</span> <span class="token operator">&lt;</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>InvalidArgumentException</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Month must be in range 1;12'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token function">is_null</span><span class="token punctuation">(</span><span class="token variable">$year</span><span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">(</span><span class="token variable">$year</span> <span class="token operator">></span> <span class="token number">9999</span> <span class="token operator">||</span> <span class="token variable">$year</span> <span class="token operator">&lt;</span> <span class="token operator">-</span><span class="token number">9999</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>InvalidArgumentException</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Year must be in range -9999;9999'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token punctuation">&#125;</span>    <span class="token variable">$date</span> <span class="token operator">=</span> <span class="token function">sprintf</span><span class="token punctuation">(</span>        <span class="token string single-quoted-string">'%s-%02s-01'</span><span class="token punctuation">,</span>         <span class="token function">is_null</span><span class="token punctuation">(</span><span class="token variable">$year</span><span class="token punctuation">)</span> <span class="token operator">?</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name class-name-fully-qualified"><span class="token punctuation">\</span>DateTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'Y'</span><span class="token punctuation">)</span> <span class="token punctuation">:</span> <span class="token variable">$year</span><span class="token punctuation">,</span>        <span class="token variable">$month</span>    <span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token keyword type-casting">int</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">DateTime</span><span class="token punctuation">(</span><span class="token variable">$date</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token function">format</span><span class="token punctuation">(</span><span class="token string single-quoted-string">'t'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span></code></pre><p>Voyons cela :</p><pre class="language-php" data-language="php"><code class="language-php"><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(28)</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2022</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(28)</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">9999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(28)</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">19999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// PHP Warning:  Uncaught InvalidArgumentException: Year must be in range -9999;9999 in php shell code:8</span><span class="token comment">// Stack trace:</span><span class="token comment">// #0 php shell code(1): daysInMonth()</span><span class="token comment">// #1 &#123;main&#125;</span><span class="token comment">//   thrown in php shell code on line 8</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">9999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(28)</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token operator">-</span><span class="token number">19999</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// PHP Warning:  Uncaught InvalidArgumentException: Year must be in range -9999;9999 in php shell code:8</span><span class="token comment">// Stack trace:</span><span class="token comment">// #0 php shell code(1): daysInMonth()</span><span class="token comment">// #1 &#123;main&#125;</span><span class="token comment">//   thrown in php shell code on line 8</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2099</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(28)</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">12</span><span class="token punctuation">,</span> <span class="token number">2099</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(31)</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">13</span><span class="token punctuation">,</span> <span class="token number">2099</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// PHP Warning:  Uncaught InvalidArgumentException: Month must be in range 1;12 in php shell code:4</span><span class="token comment">// Stack trace:</span><span class="token comment">// #0 php shell code(1): daysInMonth()</span><span class="token comment">// #1 &#123;main&#125;</span><span class="token comment">//   thrown in php shell code on line 4</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2099</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// PHP Warning:  Uncaught InvalidArgumentException: Month must be in range 1;12 in php shell code:4</span><span class="token comment">// Stack trace:</span><span class="token comment">// #0 php shell code(1): daysInMonth()</span><span class="token comment">// #1 &#123;main&#125;</span><span class="token comment">//   thrown in php shell code on line 4</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">,</span> <span class="token number">2099</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// int(31)</span><span class="token function">var_dump</span><span class="token punctuation">(</span><span class="token function">daysInMonth</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">2099</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// PHP Warning:  Uncaught InvalidArgumentException: Month must be in range 1;12 in php shell code:4</span><span class="token comment">// Stack trace:</span><span class="token comment">// #0 php shell code(1): daysInMonth()</span><span class="token comment">// #1 &#123;main&#125;</span><span class="token comment">//   thrown in php shell code on line 4</span></code></pre><p>Pas mal non ? Libre à vous de l’intégrer dans une méthode de classe par exemple.</p><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>J’espère que ces deux fonctions pourront vous être utile si vous vous retrouvezsans le module Calendar de PHP.</p>]]></content>
    
    
    <summary type="html">Fonctions pour palier le manque des fonctions easter_date() et cal_days_in_month() si vous n’avez pas accès au module Calendar de PHP.</summary>
    
    
    
    
    <category term="php" scheme="https://cogito-ergo-dev.fr/etiquettes/php/"/>
    
    <category term="date" scheme="https://cogito-ergo-dev.fr/etiquettes/date/"/>
    
    <category term="fonction" scheme="https://cogito-ergo-dev.fr/etiquettes/fonction/"/>
    
  </entry>
  
  <entry>
    <title>Pâques et autres congés avec MySQL</title>
    <link href="https://cogito-ergo-dev.fr/blog/13235/paques-et-autres-conges-avec-mysql/"/>
    <id>https://cogito-ergo-dev.fr/blog/13235/paques-et-autres-conges-avec-mysql/</id>
    <published>2022-04-16T20:23:14.000Z</published>
    <updated>2022-04-17T16:42:06.454Z</updated>
    
    <content type="html"><![CDATA[<p>Comme nous sommes en période de Pâques, je me suis souvenu que PHP propose unefonction pour retourner la date de Pâques à partir de l’année passée en argument. Et je me suis fait également la réflexion que MySQL 8 ne propose pasun équivalent. J’ai donc été voir ce que propose le monde libre à ce sujet etdu coup, j’en profite pour coder une fonction afin d’avoir les congés enFrance en fonction de la date fournie.</p><span id="more"></span><h2 id="Prerequis"><a href="#Prerequis" class="headerlink" title="Prérequis"></a>Prérequis</h2><p>Tout ce qui suit a été réalisé sous MySQL 8.</p><h2 id="Une-petite-recherche"><a href="#Une-petite-recherche" class="headerlink" title="Une petite recherche"></a>Une petite recherche</h2><p>Une recherche repide sur le Net permet d’identifier <strong>deux fonctions candidates</strong> intéressantespour le calcul de la date de Pâques en Occident (désolés les Orthodoxes, je penserai à vous possiblement dans un futur article) sous MySQL.</p><p>La première, sous <strong>licence BSD</strong>, par <a href="https://github.com/jweiher">Jan Weiher</a> dans le dépôt Github <a href="https://github.com/jweiher/mysql-easter">https://github.com/jweiher/mysql-easter</a>.</p><p>La voici, avec un petit ajout de ma part, car il faut préciser si la fonction est déterministe ou non, vu que son code est ancien, il ne contenait pas ce mot-clé :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">delimiter</span> $$<span class="token keyword">CREATE</span> <span class="token keyword">FUNCTION</span> <span class="token identifier"><span class="token punctuation">`</span>easter_f<span class="token punctuation">`</span></span><span class="token punctuation">(</span>X <span class="token keyword">INTEGER</span><span class="token punctuation">)</span> <span class="token keyword">RETURNS</span> <span class="token keyword">date</span> <span class="token keyword">deterministic</span><span class="token keyword">BEGIN</span><span class="token keyword">DECLARE</span> K<span class="token punctuation">,</span>M<span class="token punctuation">,</span>S<span class="token punctuation">,</span>A<span class="token punctuation">,</span>D<span class="token punctuation">,</span>R<span class="token punctuation">,</span>OG<span class="token punctuation">,</span>SZ<span class="token punctuation">,</span>OE<span class="token punctuation">,</span>OS <span class="token keyword">INT</span><span class="token punctuation">;</span><span class="token keyword">DECLARE</span> EASTERDATE <span class="token keyword">DATE</span><span class="token punctuation">;</span><span class="token keyword">SET</span> K <span class="token operator">=</span> X <span class="token operator">DIV</span> <span class="token number">100</span><span class="token punctuation">;</span><span class="token keyword">SET</span> M <span class="token operator">=</span> <span class="token number">15</span> <span class="token operator">+</span> <span class="token punctuation">(</span><span class="token number">3</span><span class="token operator">*</span>K <span class="token operator">+</span> <span class="token number">3</span><span class="token punctuation">)</span> <span class="token operator">DIV</span> <span class="token number">4</span> <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token number">8</span><span class="token operator">*</span>K <span class="token operator">+</span> <span class="token number">13</span><span class="token punctuation">)</span> <span class="token operator">DIV</span> <span class="token number">25</span><span class="token punctuation">;</span><span class="token keyword">SET</span> S <span class="token operator">=</span> <span class="token number">2</span> <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token number">3</span><span class="token operator">*</span>K <span class="token operator">+</span> <span class="token number">3</span><span class="token punctuation">)</span> <span class="token operator">DIV</span> <span class="token number">4</span><span class="token punctuation">;</span><span class="token keyword">SET</span> A <span class="token operator">=</span> X MOD <span class="token number">19</span><span class="token punctuation">;</span><span class="token keyword">SET</span> D <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token number">19</span><span class="token operator">*</span>A <span class="token operator">+</span> M<span class="token punctuation">)</span> MOD <span class="token number">30</span><span class="token punctuation">;</span><span class="token keyword">SET</span> R <span class="token operator">=</span> <span class="token punctuation">(</span>D <span class="token operator">+</span> A <span class="token operator">DIV</span> <span class="token number">11</span><span class="token punctuation">)</span> <span class="token operator">DIV</span> <span class="token number">29</span><span class="token punctuation">;</span><span class="token keyword">SET</span> OG <span class="token operator">=</span> <span class="token number">21</span> <span class="token operator">+</span> D <span class="token operator">-</span> R <span class="token punctuation">;</span><span class="token keyword">SET</span> SZ <span class="token operator">=</span> <span class="token number">7</span> <span class="token operator">-</span> <span class="token punctuation">(</span>X <span class="token operator">+</span> X <span class="token operator">DIV</span> <span class="token number">4</span> <span class="token operator">+</span> S<span class="token punctuation">)</span> MOD <span class="token number">7</span><span class="token punctuation">;</span><span class="token keyword">SET</span> OE <span class="token operator">=</span> <span class="token number">7</span> <span class="token operator">-</span> <span class="token punctuation">(</span>OG <span class="token operator">-</span> SZ<span class="token punctuation">)</span> MOD <span class="token number">7</span><span class="token punctuation">;</span><span class="token keyword">SET</span> OS <span class="token operator">=</span> OG <span class="token operator">+</span> OE<span class="token punctuation">;</span><span class="token keyword">SET</span> EASTERDATE <span class="token operator">=</span> date_add<span class="token punctuation">(</span>concat<span class="token punctuation">(</span>X<span class="token punctuation">,</span> <span class="token string">'-03-01'</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token keyword">INTERVAL</span> OS<span class="token operator">-</span><span class="token number">1</span> <span class="token keyword">DAY</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">RETURN</span> EASTERDATE<span class="token punctuation">;</span><span class="token keyword">END</span>$$<span class="token keyword">delimiter</span> <span class="token punctuation">;</span></code></pre><p>La fonction se crée bien sous MySQL 8 et s’exécute bien.</p><p>La seconde est celle de <a href="https://planet.mysql.com/entry/?id=30328">Scott Noyes sur Planet MySQL</a>, dont j’ai changé le typage <code>YEAR</code> en <code>INTEGER</code> pour ne pas se limiter à l’intervalle d’années 1901 à 2155 :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">DROP</span> <span class="token keyword">function</span> easter<span class="token punctuation">;</span><span class="token keyword">DELIMITER</span> <span class="token comment">//</span><span class="token keyword">CREATE</span> <span class="token keyword">FUNCTION</span> easter<span class="token punctuation">(</span>inYear <span class="token keyword">INTEGER</span><span class="token punctuation">)</span> <span class="token keyword">RETURNS</span> <span class="token keyword">DATE</span> <span class="token keyword">DETERMINISTIC</span><span class="token keyword">BEGIN</span>    <span class="token keyword">DECLARE</span> a<span class="token punctuation">,</span> b<span class="token punctuation">,</span> c<span class="token punctuation">,</span> d<span class="token punctuation">,</span> e<span class="token punctuation">,</span> k<span class="token punctuation">,</span> m<span class="token punctuation">,</span> n<span class="token punctuation">,</span> p<span class="token punctuation">,</span> q <span class="token keyword">INT</span><span class="token punctuation">;</span>    <span class="token keyword">DECLARE</span> easter <span class="token keyword">DATE</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> k <span class="token operator">=</span> FLOOR<span class="token punctuation">(</span>inYear <span class="token operator">/</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> a <span class="token operator">=</span> <span class="token function">MOD</span><span class="token punctuation">(</span>inYear<span class="token punctuation">,</span> <span class="token number">19</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> b <span class="token operator">=</span> <span class="token function">MOD</span><span class="token punctuation">(</span>inYear<span class="token punctuation">,</span> <span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> c <span class="token operator">=</span> <span class="token function">MOD</span><span class="token punctuation">(</span>inYear<span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> q <span class="token operator">=</span> FLOOR<span class="token punctuation">(</span>k <span class="token operator">/</span> <span class="token number">4</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> p <span class="token operator">=</span> FLOOR<span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">13</span> <span class="token operator">+</span> <span class="token number">8</span> <span class="token operator">*</span> k<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">25</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> m <span class="token operator">=</span> <span class="token function">MOD</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">15</span><span class="token operator">-</span>p<span class="token operator">+</span>k<span class="token operator">-</span>q<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> d <span class="token operator">=</span> <span class="token function">MOD</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">19</span> <span class="token operator">*</span> a <span class="token operator">+</span> m<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">30</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> n <span class="token operator">=</span> <span class="token function">MOD</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token operator">+</span>k<span class="token operator">-</span>q<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> e <span class="token operator">=</span> <span class="token function">MOD</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token operator">*</span>b<span class="token operator">+</span><span class="token number">4</span><span class="token operator">*</span>c<span class="token operator">+</span><span class="token number">6</span><span class="token operator">*</span>d<span class="token operator">+</span>n<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token number">7</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> easter <span class="token operator">=</span> <span class="token keyword">CASE</span>        <span class="token keyword">WHEN</span> d <span class="token operator">+</span> e <span class="token operator">&lt;=</span> <span class="token number">9</span> <span class="token keyword">THEN</span> CONCAT_WS<span class="token punctuation">(</span><span class="token string">'-'</span><span class="token punctuation">,</span> inYear<span class="token punctuation">,</span> <span class="token string">'03'</span><span class="token punctuation">,</span> <span class="token number">22</span> <span class="token operator">+</span> d <span class="token operator">+</span> e<span class="token punctuation">)</span>        <span class="token keyword">WHEN</span> d <span class="token operator">=</span> <span class="token number">29</span> <span class="token operator">AND</span> e <span class="token operator">=</span> <span class="token number">6</span> <span class="token keyword">THEN</span> CONCAT_WS<span class="token punctuation">(</span><span class="token string">'-'</span><span class="token punctuation">,</span> inYear<span class="token punctuation">,</span> <span class="token string">'04-19'</span><span class="token punctuation">)</span>        <span class="token keyword">WHEN</span> d <span class="token operator">=</span> <span class="token number">28</span> <span class="token operator">AND</span> e <span class="token operator">=</span> <span class="token number">6</span> <span class="token operator">AND</span> a <span class="token operator">></span> <span class="token number">10</span> <span class="token keyword">THEN</span> CONCAT_WS<span class="token punctuation">(</span><span class="token string">'-'</span><span class="token punctuation">,</span> inYear<span class="token punctuation">,</span> <span class="token string">'04-18'</span><span class="token punctuation">)</span>        <span class="token keyword">ELSE</span> CONCAT_WS<span class="token punctuation">(</span><span class="token string">'-'</span><span class="token punctuation">,</span> inYear<span class="token punctuation">,</span> <span class="token string">'04'</span><span class="token punctuation">,</span> LPAD<span class="token punctuation">(</span>d <span class="token operator">+</span> e <span class="token operator">-</span> <span class="token number">9</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">)</span>    <span class="token keyword">END</span><span class="token punctuation">;</span>    <span class="token keyword">RETURN</span> easter<span class="token punctuation">;</span><span class="token keyword">END</span><span class="token comment">//</span><span class="token keyword">DELIMITER</span> <span class="token punctuation">;</span></code></pre><p>Elle aussi s’installe et s’exécute correctement sur MySQL 8.</p><p>Voyons maintenant s’il y a des différences (bugs donc) entre les deux en les testant à partir de l’année 1200 et pour les 1000 ans suivant :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">with</span> recursive cte_years <span class="token keyword">as</span> <span class="token punctuation">(</span>  <span class="token keyword">select</span> <span class="token number">1200</span> y<span class="token punctuation">,</span> <span class="token number">1</span> inc   <span class="token keyword">union</span> <span class="token keyword">all</span>  <span class="token keyword">select</span> y <span class="token operator">+</span> <span class="token number">1</span> y<span class="token punctuation">,</span> inc <span class="token operator">+</span> <span class="token number">1</span> inc <span class="token keyword">from</span> cte_years <span class="token keyword">where</span> inc <span class="token operator">&lt;</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">,</span>cte_compare <span class="token keyword">as</span> <span class="token punctuation">(</span>  <span class="token keyword">select</span> easter_f<span class="token punctuation">(</span>y<span class="token punctuation">)</span> <span class="token operator">=</span> easter<span class="token punctuation">(</span>y<span class="token punctuation">)</span> test <span class="token keyword">from</span> cte_years<span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token operator">*</span> <span class="token keyword">from</span> cte_compare <span class="token keyword">where</span> test <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span></code></pre><p>Ce qui donne :</p><pre class="language-none"><code class="language-none">Empty set (0,06 sec)</code></pre><p>OK, même résultat. Voyons en terme de performance maintenant :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">with</span> recursive cte_years <span class="token keyword">as</span> <span class="token punctuation">(</span>  <span class="token keyword">select</span> <span class="token number">1200</span> y<span class="token punctuation">,</span> <span class="token number">1</span> inc   <span class="token keyword">union</span> <span class="token keyword">all</span>  <span class="token keyword">select</span> y <span class="token operator">+</span> <span class="token number">1</span> y<span class="token punctuation">,</span> inc <span class="token operator">+</span> <span class="token number">1</span> inc <span class="token keyword">from</span> cte_years <span class="token keyword">where</span> inc <span class="token operator">&lt;</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">,</span>cte_compare <span class="token keyword">as</span> <span class="token punctuation">(</span>  <span class="token keyword">select</span> easter_f<span class="token punctuation">(</span>y<span class="token punctuation">)</span> <span class="token keyword">from</span> cte_years<span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token operator">*</span> <span class="token keyword">from</span> cte_compare<span class="token punctuation">;</span></code></pre><p>La première fonction donne un temps d’exécution de 0,03 ms.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">with</span> recursive cte_years <span class="token keyword">as</span> <span class="token punctuation">(</span>  <span class="token keyword">select</span> <span class="token number">1200</span> y<span class="token punctuation">,</span> <span class="token number">1</span> inc   <span class="token keyword">union</span> <span class="token keyword">all</span>  <span class="token keyword">select</span> y <span class="token operator">+</span> <span class="token number">1</span> y<span class="token punctuation">,</span> inc <span class="token operator">+</span> <span class="token number">1</span> inc <span class="token keyword">from</span> cte_years <span class="token keyword">where</span> inc <span class="token operator">&lt;</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">,</span>cte_compare <span class="token keyword">as</span> <span class="token punctuation">(</span>  <span class="token keyword">select</span> easter<span class="token punctuation">(</span>y<span class="token punctuation">)</span> <span class="token keyword">from</span> cte_years<span class="token punctuation">)</span><span class="token keyword">select</span> <span class="token operator">*</span> <span class="token keyword">from</span> cte_compare<span class="token punctuation">;</span></code></pre><p>La deuxième met 0,04 ms. Petite différence, mais cela peut jouer pour des appels fréquents.Je vais utiliser la première.</p><h2 id="Exploitons-ca"><a href="#Exploitons-ca" class="headerlink" title="Exploitons ça !"></a>Exploitons ça !</h2><p>Maintenant que nous avons une fonction toute prête pour nous retourner la datede Pâques pour une année donnée, on peut déterminer les dates d’autres jours fériés qui dépendent de Pâques :</p><ul><li>le lundi de Pâques, 1 jour après Pâques,</li><li>l’Ascension, 39 jours après Pâques,</li><li>et la Pentecôte, 49 jours après Pâques.</li></ul><p>Pour les autres, ce sont des dates fixes :</p><ul><li>Jour de l’an, le permier janvier,</li><li>Fête des Travailleurs, le premier mai,</li><li>Victoire des Alliés, le 8 mai</li><li>Fête Nationale, le 14 juillet,</li><li>Assomption, le 15 août,</li><li>Toussaint, le permier novembre</li><li>Armistice, le 11 novembre</li><li>et enfin Noël, le 25 décembre.</li></ul><p>Notre fonction devra prendre une date en entrée, et en sortie donner le nom dujour férié (en <code>STRING</code>) ou <code>NULL</code> si le jour ne correspond pas à un jour férié.</p><p>Un exemple d’implémentation pourrait être le suivant :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">DELIMITER</span> <span class="token comment">//</span><span class="token keyword">CREATE</span> <span class="token keyword">FUNCTION</span> <span class="token identifier"><span class="token punctuation">`</span>is_fr_public_holiday<span class="token punctuation">`</span></span><span class="token punctuation">(</span>someday <span class="token keyword">DATE</span><span class="token punctuation">)</span> <span class="token keyword">RETURNS</span> <span class="token keyword">varchar</span><span class="token punctuation">(</span><span class="token number">25</span><span class="token punctuation">)</span>    <span class="token keyword">DETERMINISTIC</span><span class="token keyword">BEGIN</span>    <span class="token comment">-- oui c’est franco-français, alors les variables sont en français :)</span><span class="token keyword">DECLARE</span> annee <span class="token keyword">INTEGER</span><span class="token punctuation">;</span><span class="token keyword">DECLARE</span> mois <span class="token keyword">INTEGER</span><span class="token punctuation">;</span><span class="token keyword">DECLARE</span> jour <span class="token keyword">INTEGER</span><span class="token punctuation">;</span>    <span class="token keyword">DECLARE</span> paques <span class="token keyword">DATE</span><span class="token punctuation">;</span>    <span class="token keyword">DECLARE</span> lundi_de_paques <span class="token keyword">DATE</span><span class="token punctuation">;</span>    <span class="token keyword">DECLARE</span> ascension <span class="token keyword">DATE</span><span class="token punctuation">;</span>    <span class="token keyword">DECLARE</span> pentecote <span class="token keyword">DATE</span><span class="token punctuation">;</span>        <span class="token keyword">SET</span> annee <span class="token operator">=</span> <span class="token keyword">YEAR</span><span class="token punctuation">(</span>someday<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">SET</span> mois <span class="token operator">=</span> <span class="token keyword">MONTH</span><span class="token punctuation">(</span>someday<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> jour <span class="token operator">=</span> <span class="token keyword">DAY</span><span class="token punctuation">(</span>someday<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token comment">-- on commence par les cas simples</span>    <span class="token keyword">IF</span> mois <span class="token operator">=</span> <span class="token number">1</span> <span class="token operator">AND</span> jour <span class="token operator">=</span> <span class="token number">1</span> <span class="token keyword">THEN</span>         <span class="token keyword">RETURN</span> <span class="token string">'Jour de l’an'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> mois <span class="token operator">=</span> <span class="token number">5</span> <span class="token operator">AND</span> jour <span class="token operator">=</span> <span class="token number">1</span> <span class="token keyword">THEN</span>         <span class="token keyword">RETURN</span> <span class="token string">'Fête des Travailleurs'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> mois <span class="token operator">=</span> <span class="token number">5</span> <span class="token operator">AND</span> jour <span class="token operator">=</span> <span class="token number">8</span> <span class="token keyword">THEN</span>         <span class="token keyword">RETURN</span> <span class="token string">'Victoire des Alliés'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> mois <span class="token operator">=</span> <span class="token number">7</span> <span class="token operator">AND</span> jour <span class="token operator">=</span> <span class="token number">14</span> <span class="token keyword">THEN</span>         <span class="token keyword">RETURN</span> <span class="token string">'Fête Nationale'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> mois <span class="token operator">=</span> <span class="token number">8</span> <span class="token operator">AND</span> jour <span class="token operator">=</span> <span class="token number">15</span> <span class="token keyword">THEN</span>         <span class="token keyword">RETURN</span> <span class="token string">'Assomption'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> mois <span class="token operator">=</span> <span class="token number">11</span> <span class="token operator">AND</span> jour <span class="token operator">=</span> <span class="token number">1</span> <span class="token keyword">THEN</span>         <span class="token keyword">RETURN</span> <span class="token string">'Toussaint'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> mois <span class="token operator">=</span> <span class="token number">11</span> <span class="token operator">AND</span> jour <span class="token operator">=</span> <span class="token number">11</span> <span class="token keyword">THEN</span>         <span class="token keyword">RETURN</span> <span class="token string">'Armistice'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> mois <span class="token operator">=</span> <span class="token number">12</span> <span class="token operator">AND</span> jour <span class="token operator">=</span> <span class="token number">25</span> <span class="token keyword">THEN</span>         <span class="token keyword">RETURN</span> <span class="token string">'Noël'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> paques <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> easter_f<span class="token punctuation">(</span>annee<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> lundi_de_paques <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> date_add<span class="token punctuation">(</span>paques<span class="token punctuation">,</span> <span class="token keyword">INTERVAL</span> <span class="token operator">+</span><span class="token number">1</span> <span class="token keyword">DAY</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> ascension <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> date_add<span class="token punctuation">(</span>paques<span class="token punctuation">,</span> <span class="token keyword">INTERVAL</span> <span class="token operator">+</span><span class="token number">39</span> <span class="token keyword">DAY</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">SET</span> pentecote <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> date_add<span class="token punctuation">(</span>paques<span class="token punctuation">,</span> <span class="token keyword">INTERVAL</span> <span class="token operator">+</span><span class="token number">49</span> <span class="token keyword">DAY</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> someday <span class="token operator">=</span> paques <span class="token keyword">THEN</span>        <span class="token keyword">RETURN</span> <span class="token string">'Pâques'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> someday <span class="token operator">=</span> lundi_de_paques <span class="token keyword">THEN</span>        <span class="token keyword">RETURN</span> <span class="token string">'Lundi de Pâques'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> someday <span class="token operator">=</span> ascension <span class="token keyword">THEN</span>        <span class="token keyword">RETURN</span> <span class="token string">'Ascension'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>    <span class="token keyword">IF</span> someday <span class="token operator">=</span> pentecote <span class="token keyword">THEN</span>        <span class="token keyword">RETURN</span> <span class="token string">'Pentecôte'</span><span class="token punctuation">;</span>    <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span><span class="token keyword">RETURN</span> <span class="token boolean">NULL</span><span class="token punctuation">;</span><span class="token keyword">END</span> <span class="token comment">//</span><span class="token keyword">DELIMITER</span> <span class="token punctuation">;</span></code></pre><p>Testons avec la fête du moment :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> is_fr_public_holiday<span class="token punctuation">(</span><span class="token string">'2022-04-17'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Qui donne :</p><pre class="language-none"><code class="language-none">+------------------------------------+| is_fr_public_holiday(&#39;2022-04-17&#39;) |+------------------------------------+| Pâques                             |+------------------------------------+1 row in set (0,01 sec)</code></pre><p>Le lundi de Pâque :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> is_fr_public_holiday<span class="token punctuation">(</span><span class="token string">'2022-04-18'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><pre class="language-none"><code class="language-none">+------------------------------------+| is_fr_public_holiday(&#39;2022-04-18&#39;) |+------------------------------------+| Lundi de Pâques                    |+------------------------------------+1 row in set (0,00 sec)</code></pre><p>Un jour quelconque :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> is_fr_public_holiday<span class="token punctuation">(</span><span class="token string">'1978-01-14'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><pre class="language-none"><code class="language-none">+------------------------------------+| is_fr_public_holiday(&#39;1978-01-14&#39;) |+------------------------------------+| NULL                               |+------------------------------------+1 row in set (0,00 sec)</code></pre><p>Ça ne semble pas mal non ?</p><p>Du coup, pour toute l’année courante, nous pouvons faire la requête suivante pour avoir les jours fériés ou non :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">with</span> recursive cte_current_year <span class="token keyword">as</span> <span class="token punctuation">(</span>    <span class="token keyword">select</span>  cast<span class="token punctuation">(</span>concat<span class="token punctuation">(</span><span class="token keyword">year</span><span class="token punctuation">(</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">'-01-01'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">date</span><span class="token punctuation">)</span> d    <span class="token keyword">union</span> <span class="token keyword">all</span>    <span class="token keyword">select</span>  cast<span class="token punctuation">(</span>date_add<span class="token punctuation">(</span>d<span class="token punctuation">,</span> <span class="token keyword">interval</span> <span class="token operator">+</span><span class="token number">1</span> <span class="token keyword">day</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">date</span><span class="token punctuation">)</span> d    <span class="token keyword">from</span>    cte_current_year    <span class="token keyword">where</span>   d <span class="token operator">&lt;</span> concat<span class="token punctuation">(</span><span class="token keyword">year</span><span class="token punctuation">(</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token string">'-12-31'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>cte_name <span class="token keyword">as</span> <span class="token punctuation">(</span>    <span class="token keyword">select</span>  d<span class="token punctuation">,</span>             is_fr_public_holiday<span class="token punctuation">(</span>d<span class="token punctuation">)</span> holiday_name    <span class="token keyword">from</span>    cte_current_year<span class="token punctuation">)</span><span class="token keyword">select</span>  d<span class="token punctuation">,</span>        holiday_name<span class="token punctuation">,</span>        holiday_name <span class="token operator">is</span> <span class="token operator">not</span> <span class="token boolean">null</span> is_holiday<span class="token keyword">from</span>    cte_name<span class="token punctuation">;</span></code></pre><p>Ce qui nous donne, en tronqué :</p><pre class="language-none"><code class="language-none">+------------+------------------------+------------+| d          | holiday_name           | is_holiday |+------------+------------------------+------------+| 2022-01-01 | Jour de l’an           |          1 || 2022-01-02 | NULL                   |          0 || 2022-01-03 | NULL                   |          0 |[...]| 2022-04-15 | NULL                   |          0 || 2022-04-16 | NULL                   |          0 || 2022-04-17 | Pâques                 |          1 || 2022-04-18 | Lundi de Pâques        |          1 || 2022-04-19 | NULL                   |          0 || 2022-04-20 | NULL                   |          0 |[...]| 2022-04-28 | NULL                   |          0 || 2022-04-29 | NULL                   |          0 || 2022-04-30 | NULL                   |          0 || 2022-05-01 | Fête des Travailleurs  |          1 || 2022-05-02 | NULL                   |          0 || 2022-05-03 | NULL                   |          0 || 2022-05-04 | NULL                   |          0 || 2022-05-05 | NULL                   |          0 || 2022-05-06 | NULL                   |          0 || 2022-05-07 | NULL                   |          0 || 2022-05-08 | Victoire des Alliés    |          1 || 2022-05-09 | NULL                   |          0 || 2022-05-10 | NULL                   |          0 |[...]| 2022-05-24 | NULL                   |          0 || 2022-05-25 | NULL                   |          0 || 2022-05-26 | Ascension              |          1 || 2022-05-27 | NULL                   |          0 || 2022-05-28 | NULL                   |          0 |[...]| 2022-06-03 | NULL                   |          0 || 2022-06-04 | NULL                   |          0 || 2022-06-05 | Pentecôte              |          1 || 2022-06-06 | NULL                   |          0 || 2022-06-07 | NULL                   |          0 |[...]| 2022-07-12 | NULL                   |          0 || 2022-07-13 | NULL                   |          0 || 2022-07-14 | Fête Nationale         |          1 || 2022-07-15 | NULL                   |          0 || 2022-07-16 | NULL                   |          0 |[...]| 2022-08-13 | NULL                   |          0 || 2022-08-14 | NULL                   |          0 || 2022-08-15 | Assomption             |          1 || 2022-08-16 | NULL                   |          0 || 2022-08-17 | NULL                   |          0 |[...]| 2022-10-30 | NULL                   |          0 || 2022-10-31 | NULL                   |          0 || 2022-11-01 | Toussaint              |          1 || 2022-11-02 | NULL                   |          0 || 2022-11-03 | NULL                   |          0 |[...]| 2022-11-09 | NULL                   |          0 || 2022-11-10 | NULL                   |          0 || 2022-11-11 | Armistice              |          1 || 2022-11-12 | NULL                   |          0 || 2022-11-13 | NULL                   |          0 |[...]| 2022-12-23 | NULL                   |          0 || 2022-12-24 | NULL                   |          0 || 2022-12-25 | Noël                   |          1 || 2022-12-26 | NULL                   |          0 || 2022-12-27 | NULL                   |          0 || 2022-12-28 | NULL                   |          0 || 2022-12-29 | NULL                   |          0 || 2022-12-30 | NULL                   |          0 || 2022-12-31 | NULL                   |          0 |+------------+------------------------+------------+365 rows in set (0,05 sec)</code></pre><h2 id="Calendrier-des-dates-de-jours-feries"><a href="#Calendrier-des-dates-de-jours-feries" class="headerlink" title="Calendrier des dates de jours fériés"></a>Calendrier des dates de jours fériés</h2><p>La dernière requête est intéressante : intégrons-la dans une procédure stockée qui prend comme entrée l’année désirée :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">DELIMITER</span> <span class="token comment">//</span><span class="token keyword">CREATE</span> <span class="token keyword">PROCEDURE</span> cal_fr_public_holiday<span class="token punctuation">(</span><span class="token operator">IN</span> y <span class="token keyword">YEAR</span><span class="token punctuation">)</span><span class="token keyword">BEGIN</span>    <span class="token keyword">with</span> recursive cte_current_year <span class="token keyword">as</span> <span class="token punctuation">(</span>        <span class="token keyword">select</span>  cast<span class="token punctuation">(</span>concat<span class="token punctuation">(</span>y<span class="token punctuation">,</span> <span class="token string">'-01-01'</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">date</span><span class="token punctuation">)</span> d        <span class="token keyword">union</span> <span class="token keyword">all</span>        <span class="token keyword">select</span>  cast<span class="token punctuation">(</span>date_add<span class="token punctuation">(</span>d<span class="token punctuation">,</span> <span class="token keyword">interval</span> <span class="token operator">+</span><span class="token number">1</span> <span class="token keyword">day</span><span class="token punctuation">)</span> <span class="token keyword">as</span> <span class="token keyword">date</span><span class="token punctuation">)</span> d        <span class="token keyword">from</span>    cte_current_year        <span class="token keyword">where</span>   d <span class="token operator">&lt;</span> concat<span class="token punctuation">(</span>y<span class="token punctuation">,</span> <span class="token string">'-12-31'</span><span class="token punctuation">)</span>    <span class="token punctuation">)</span><span class="token punctuation">,</span>    cte_name <span class="token keyword">as</span> <span class="token punctuation">(</span>        <span class="token keyword">select</span>  d<span class="token punctuation">,</span>                 is_fr_public_holiday<span class="token punctuation">(</span>d<span class="token punctuation">)</span> holiday_name        <span class="token keyword">from</span>    cte_current_year    <span class="token punctuation">)</span><span class="token punctuation">,</span>    cte_filter <span class="token keyword">as</span> <span class="token punctuation">(</span>        <span class="token keyword">select</span>  d<span class="token punctuation">,</span>                holiday_name<span class="token punctuation">,</span>                holiday_name <span class="token operator">is</span> <span class="token operator">not</span> <span class="token boolean">null</span> is_holiday        <span class="token keyword">from</span>    cte_name    <span class="token punctuation">)</span>    <span class="token keyword">select</span>  d <span class="token identifier"><span class="token punctuation">`</span>day<span class="token punctuation">`</span></span><span class="token punctuation">,</span>             <span class="token identifier"><span class="token punctuation">`</span>holiday_name<span class="token punctuation">`</span></span>     <span class="token keyword">from</span>    cte_filter    <span class="token keyword">where</span>   is_holiday <span class="token operator">=</span> <span class="token number">1</span>    <span class="token punctuation">;</span><span class="token keyword">END</span><span class="token keyword">DELIMITER</span> <span class="token punctuation">;</span></code></pre><p>Remarquez que cette-fois-ci je signe la procédure avec le type <code>YEAR</code> et non <code>INTEGER</code>, car certaines fêtes n’ont pas de sens trop loin dans le passé (1er mai, Armistice, etc.)</p><p>Exécutons cette nouvelle procédure :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">call</span> cal_fr_public_holiday<span class="token punctuation">(</span><span class="token number">2022</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Ce qui donne :</p><pre class="language-none"><code class="language-none">+------------+------------------------+| day        | holiday_name           |+------------+------------------------+| 2022-01-01 | Jour de l’an           || 2022-04-17 | Pâques                 || 2022-04-18 | Lundi de Pâques        || 2022-05-01 | Fête des Travailleurs  || 2022-05-08 | Victoire des Alliés    || 2022-05-26 | Ascension              || 2022-06-05 | Pentecôte              || 2022-07-14 | Fête Nationale         || 2022-08-15 | Assomption             || 2022-11-01 | Toussaint              || 2022-11-11 | Armistice              || 2022-12-25 | Noël                   |+------------+------------------------+12 rows in set (0,03 sec)</code></pre><p>Cela pourrait être affiné en tenant compte de l’année d’apparition de certains congés. Mais dans la majeure partie des cas, ce code suffira amplement.</p><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>Nous avons découvert <strong>la très pratique fonction de Jan Weiher</strong> retournant la date de Pâques pour une année donnée, puis nous avons <strong>créé une autre fonction</strong> l’utilisant pour déterminer <strong>si une date donnée correspond à un jour férié en France</strong> et enfin, nous avons une procédure stockée permettant d’avoir <strong>la liste des jours fériés en France pour une année donnée</strong>.</p><p>Vous n’avez plus d’excuse pour oublier vos jours fériés maintenant !</p>]]></content>
    
    
    <summary type="html">Pâques et les jours fériés dans MySQL, avec fonction et procédure stockée.</summary>
    
    
    
    
    <category term="db" scheme="https://cogito-ergo-dev.fr/etiquettes/db/"/>
    
    <category term="fonction" scheme="https://cogito-ergo-dev.fr/etiquettes/fonction/"/>
    
    <category term="mysql" scheme="https://cogito-ergo-dev.fr/etiquettes/mysql/"/>
    
  </entry>
  
  <entry>
    <title>Geler les tableaux en JavaScript</title>
    <link href="https://cogito-ergo-dev.fr/blog/42001/geler-les-tableaux-en-javascript/"/>
    <id>https://cogito-ergo-dev.fr/blog/42001/geler-les-tableaux-en-javascript/</id>
    <published>2021-03-02T21:19:26.000Z</published>
    <updated>2022-04-12T21:12:25.226Z</updated>
    
    <content type="html"><![CDATA[<p>Avoir des variables immuables permet d’éviter certains problèmes pénibles en programmation.C’est un des aspects de la programmation fonctionnelle. Donc, si vous voulez faire de laprogrammation fonctionnelle en JavaScript, cet article vous montre déjà commentavoir des variables immuables pour les types complexes que sont les tableaux de valeurs.</p><span id="more"></span><h2 id="Declaration-de-variables-en-JavaScript"><a href="#Declaration-de-variables-en-JavaScript" class="headerlink" title="Déclaration de variables en JavaScript"></a>Déclaration de variables en JavaScript</h2><p>JavaScript permet de déclarer les variables selon 3 méthodes différentes :</p><ul><li><code>var myVariable;</code></li><li><code>let myVariable;</code></li><li><code>const myVariable;</code></li></ul><p>L’utilisation de <code>var</code> est fortement déconseillée maintenant. Préférez l’utilisationde <code>let</code> pour une variable qui pourra être amenée à être modifiée&#x2F;écrasée et <code>const</code>pour une variable ne devant pas changer.</p><p>Et non, l’utilisation de <code>const</code> seul ne suffit pas pour des tableaux de données.Sinon ce serait trop simple.</p><p>Pour avoir vraiment un tableau gelé, il faut utiliser également <code>Object.freeze()</code>.</p><h2 id="L’exemple"><a href="#L’exemple" class="headerlink" title="L’exemple"></a>L’exemple</h2><p>Nous allons nous appuyer sur ces quatres déclarations de variables d’un même tableau comportant 3 éléments via les mots-clés <code>let</code> ou <code>const</code>, avec ou sans l’appel de <code>Object.freeze()</code> :</p><pre class="language-javascript" data-language="javascript"><code class="language-javascript"><span class="token keyword">let</span> foo <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token string">'b'</span><span class="token punctuation">,</span> <span class="token string">'c'</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">const</span> foo <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token string">'b'</span><span class="token punctuation">,</span> <span class="token string">'c'</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token keyword">let</span> foo <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">freeze</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token string">'b'</span><span class="token punctuation">,</span> <span class="token string">'c'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">const</span> foo <span class="token operator">=</span> Object<span class="token punctuation">.</span><span class="token function">freeze</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'a'</span><span class="token punctuation">,</span> <span class="token string">'b'</span><span class="token punctuation">,</span> <span class="token string">'c'</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><h2 id="Le-code-execute-apres-chaque-declaration"><a href="#Le-code-execute-apres-chaque-declaration" class="headerlink" title="Le code exécuté après chaque déclaration"></a>Le code exécuté après chaque déclaration</h2><p>Le code va tenter :</p><ul><li>de réaffecter le premier item du tableau avec une nouvelle valeur, </li><li>d’ajouter une nouvelle valeur à la fin du taleau,</li><li>de remplacer complètement le tableau par un autre.</li></ul><p>Via des <code>console.table()</code> et des <code>try … catch()</code>, nous allons observer ce qui se passe…</p><p>Voici le code en question :</p><pre class="language-javascript" data-language="javascript"><code class="language-javascript"><span class="token keyword">try</span> <span class="token punctuation">&#123;</span>foo<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">'d'</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span> <span class="token keyword">catch</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Fail assigning "d" value on index 0.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span><span class="token keyword">try</span> <span class="token punctuation">&#123;</span>foo<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token string">'e'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Fail pushing new "e" value.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span>console<span class="token punctuation">.</span><span class="token function">table</span><span class="token punctuation">(</span>foo<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">try</span> <span class="token punctuation">&#123;</span>foo <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token string">'r'</span><span class="token punctuation">,</span> <span class="token string">'e'</span><span class="token punctuation">,</span> <span class="token string">'s'</span><span class="token punctuation">,</span> <span class="token string">'e'</span><span class="token punctuation">,</span> <span class="token string">'t'</span><span class="token punctuation">]</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span>e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Fail reassigning variable using full array.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">&#125;</span>console<span class="token punctuation">.</span><span class="token function">table</span><span class="token punctuation">(</span>foo<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Et maintenant, testons ça !</p><h2 id="let-seul"><a href="#let-seul" class="headerlink" title="let seul"></a>let seul</h2><p>Sans trop de surprise, un simple <code>let</code> permet de tout faire sans erreur, pasd’exception levée, et les modifications demandées se sont réalisées.</p><pre class="language-text" data-language="text"><code class="language-text">'d'4+---------+--------+| (index) | Values |+---------+--------+|    0    |  'd'   ||    1    |  'b'   ||    2    |  'c'   ||    3    |  'e'   |+---------+--------+[ 'r', 'e', 's', 'e', 't' ]+---------+--------+| (index) | Values |+---------+--------+|    0    |  'r'   ||    1    |  'e'   ||    2    |  's'   ||    3    |  'e'   ||    4    |  't'   |+---------+--------+</code></pre><h2 id="const-seul"><a href="#const-seul" class="headerlink" title="const seul"></a>const seul</h2><p>Avec <code>const</code>, comme pour <code>let</code>, lajout et la modification d’item au sein du tableau est possible.</p><p>Par contre, l’affection d’une nouvelle variable à la place du tableau échoue.</p><pre class="language-text" data-language="text"><code class="language-text">'d'4+---------+--------+| (index) | Values |+---------+--------+|    0    |  'd'   ||    1    |  'b'   ||    2    |  'c'   ||    3    |  'e'   |+---------+--------+Fail reassigning variable using full array.+---------+--------+| (index) | Values |+---------+--------+|    0    |  'd'   ||    1    |  'b'   ||    2    |  'c'   ||    3    |  'e'   |+---------+--------+</code></pre><p>Avec <code>const</code>, vous préservez le tableau en temps que tel, mais pas son contenu.</p><h2 id="let-Object-freeze"><a href="#let-Object-freeze" class="headerlink" title="let + Object.freeze"></a>let + Object.freeze</h2><p>Cette fois, il est impossible de modifier ou d’ajouter un item au tableau.</p><p>En revanche, il est possible de subtituer le tableau d’origine par un autre…</p><p>Ce n’est donc toujours pas une variable 100% immuable.</p><pre class="language-text" data-language="text"><code class="language-text">'d'Fail pushing new "e" value.+---------+--------+| (index) | Values |+---------+--------+|    0    |  'a'   ||    1    |  'b'   ||    2    |  'c'   |+---------+--------++---------+--------+| (index) | Values |+---------+--------+|    0    |  'r'   ||    1    |  'e'   ||    2    |  's'   ||    3    |  'e'   ||    4    |  't'   |+---------+--------+</code></pre><p>Notez que le forçage de valeur pour l’index 0 ne soulève aucune erreur et que lavaleur n’est pas écrasée, alors que la tentative d’ajout se solde par la levée d’une exception.</p><h2 id="const-Object-freeze"><a href="#const-Object-freeze" class="headerlink" title="const + Object.freeze"></a>const + Object.freeze</h2><p>Cette fois, l’utilisation conjointe de <code>const</code> et de <code>Object.freeze()</code> ne permet aucune modification :</p><ul><li>impossible d’jouter un item</li><li>impossible de modifier un item</li><li>impossible de changer la variable dans son ensemble.</li></ul><p>Nous avons donc un vrai objet immuable !</p><pre class="language-text" data-language="text"><code class="language-text">'d'Fail pushing new "e" value.+---------+--------+| (index) | Values |+---------+--------+|    0    |  'a'   ||    1    |  'b'   ||    2    |  'c'   |+---------+--------+Fail reassigning variable using full array.+---------+--------+| (index) | Values |+---------+--------+|    0    |  'a'   ||    1    |  'b'   ||    2    |  'c'   |+---------+--------+</code></pre><h3 id="Recapitulons"><a href="#Recapitulons" class="headerlink" title="Récapitulons"></a>Récapitulons</h3><p>L’utilisation de <code>let</code>, <code>const</code> et <code>Object.freeze()</code> donne un gradient demodifications possibles qui se résume bien dans le tableau suivant :</p><table class="uk-table uk-table-divider">  <thead>    <tr>      <th>Déclaration</th>      <th>Changement item(s)</th>      <th>Changement total</th>    </tr>  </thead>  <tbody>  <tr>    <th>let</th>    <td>Oui</td>    <td>Oui</td>  </tr>  <tr>    <th>const</th>    <td>Oui</td>    <td>Non</td>  </tr>  <tr>    <th>let + Object.freeze()</th>    <td>Non</td>    <td>Oui</td>  </tr>  <tr>    <th>const + Object.freeze()</th>    <td>Non</td>    <td>Non</td>  </tr>  </tbody></table><p>En fonction de l’usage que vous en aurez, tel ou tel gradient peut mieux convenir.</p><p>Le plus clair étant celui qui ne permet pas de modification, avec <code>const</code> et <code>Object.freeze()</code>.</p><p><strong>Note :</strong> Bien que ne parlant que de tableaux, ces résultats sont aussi vrai pour des objets en JavaScript.</p>]]></content>
    
    
    <summary type="html">Comment rendre immuable les variables de type objet ou tableau en JavaScript ? Cet article y répond.</summary>
    
    
    
    
    <category term="javascript" scheme="https://cogito-ergo-dev.fr/etiquettes/javascript/"/>
    
    <category term="fp" scheme="https://cogito-ergo-dev.fr/etiquettes/fp/"/>
    
  </entry>
  
  <entry>
    <title>Mettre en place le protocole HTTP/2</title>
    <link href="https://cogito-ergo-dev.fr/blog/5897/mettre-en-place-le-protocole-http-2/"/>
    <id>https://cogito-ergo-dev.fr/blog/5897/mettre-en-place-le-protocole-http-2/</id>
    <published>2021-01-22T11:23:06.000Z</published>
    <updated>2022-04-12T21:12:25.234Z</updated>
    
    <content type="html"><![CDATA[<p>Les sites web doivent être de plus en plus légers et de plus en plus rapides afind’optimiser le SEO et le temps de chargement sur mobile. Réduction de la tailledu DOM, du poids des images et… optimisation des requêtes HTTP grâce au protocoleHTTP&#x2F;2. Voyons comment l’activer.</p><span id="more"></span><h2 id="Prerequis"><a href="#Prerequis" class="headerlink" title="Prérequis"></a>Prérequis</h2><p>Les exemples fournis se basent sur un site web utilisant le serveur web et tournantsous PHP.</p><p>Le système d’exploitation est une Ubuntu 18.04.</p><h2 id="Est-ce-complique-a-mettre-en-place"><a href="#Est-ce-complique-a-mettre-en-place" class="headerlink" title="Est-ce compliqué à mettre en place ?"></a>Est-ce compliqué à mettre en place ?</h2><p>Oui et non. Tout dépend comment vous allez tirer le meilleur de ce protocole.Si vous voulez juste l’activer pour avoir les performances de base, c’est assezsimple, si vous voulez le <em>push</em>, alors il va falloir réflèchir un peu à ce quidoit être envoyé au client par cette voie (et attention, le <em>push</em> peut apporterplus d’inconvénients que d’avantages).</p><h2 id="Prerequis-1"><a href="#Prerequis-1" class="headerlink" title="Prérequis"></a>Prérequis</h2><p>Il vous faut <strong>impérativement le SSL</strong>, autrement dit, <strong>sans HTTPS, pas de HTTP&#x2F;2</strong>possible, car il nécessite TLS qui est au sein de HTTPS.</p><p>Si vous n’avez pas le budget, passer par <a href="https://letsencrypt.org/fr/">Let’s encrypt</a>pour avoir votre site servi en HTTPS gratuitement. Au passage, si vous appréciezleur service, envisagez de leur <a href="https://letsencrypt.org/fr/donate/">faire un don</a>…</p><p>Enfin, je vais considérer dans cet article la mise en place de HTTP&#x2F;2 pour un site en PHP 7.2 servi par Apache 2 sur une Ubuntu LTS 18.04. Si vous avez uneautre configuration :</p><ul><li><p>Il vous faut <strong>Apache version 2.4.24</strong> au minimum, car c’est cette versiond’apache qui supporte pour la première fois le protocole HTTP&#x2F;2.</p></li><li><p>Vous devez utiliser le <strong>module PHP FPM</strong>, sinon, il sera impossible d’activerle protocole HTTP&#x2F;2, même si vous le spécifiez en configuration, il resterasur le protocole HTTP&#x2F;1.1.</p></li></ul><h2 id="Desinstallation-des-paquets-non-compatibles"><a href="#Desinstallation-des-paquets-non-compatibles" class="headerlink" title="Désinstallation des paquets non compatibles"></a>Désinstallation des paquets non compatibles</h2><pre class="language-none"><code class="language-none">sudo a2dismod php7.2sudo a2dismod mpm_prefork</code></pre><h2 id="Installation-des-paquets-compatibles"><a href="#Installation-des-paquets-compatibles" class="headerlink" title="Installation des paquets compatibles"></a>Installation des paquets compatibles</h2><pre class="language-none"><code class="language-none">a2enmod http2apt-get install php7.2-fpma2enmod proxy_fcgi setenvifa2enconf php7.2-fpma2dismod php7.2a2dismod mpm_preforka2enmod mpm_eventsystemctl reload apache2</code></pre><h2 id="Ajout-du-protocole-HTTP-x2F-2-sur-un-site"><a href="#Ajout-du-protocole-HTTP-x2F-2-sur-un-site" class="headerlink" title="Ajout du protocole HTTP&#x2F;2 sur un site"></a>Ajout du protocole HTTP&#x2F;2 sur un site</h2><p>Il suffit d’ajouter cette ligne dans le fichier de configuration d’un<em>Virtual Host</em>, par exemple juste après la ligne d’ouverture de labalise <code>&lt;VirtualHost *:443&gt;</code> :</p><pre class="language-apache" data-language="apache"><code class="language-apache">Protocols h2 http&#x2F;1.1</code></pre><p>Et voilà, en principe, HTTP&#x2F;2 sera disponible après un <code>systemctl reload apache2</code>.Pour vérifier cela, utilisez les <em>Dev Tools</em> disponibles sur les navigateursweb Firefox ou Chrome.</p><p>Considérons avec Firefox l’exemple du chargement de <a href="https://fr.wikipedia.org/wiki/Hypertext_Transfer_Protocol/2">la page Wikipedia sur le protocole HTTP2</a> :</p>    <picture>        <source srcset="./firefox-dev-tools-http2.webp" type="image/webp">        <img            src="./firefox-dev-tools-http2.png" type="image/png"            alt="Capture d’écran des évènements réseaux dans les outils Dev Tools de Firefox pour voir l’activation du protocole HTTP/2"  class="uk-margin-auto uk-display-block" uk-img        >    </picture>    <p>Félicitation, comme le montre la colonne « Protocole », votre site est maintenantconfiguré pour servir du contenu en HTTP&#x2F;2.0 !</p><h2 id="Et-pour-nginx"><a href="#Et-pour-nginx" class="headerlink" title="Et pour nginx ?"></a>Et pour nginx ?</h2><p>Apparemment, dixit <a href="https://www.webrankinfo.com/forum/t/passer-en-http-2-avantages-dangers.194345/#post-1581988">spout dans un post sur le forum Web Rank Info</a>, c’est aussi simple que ça :</p><pre class="language-nginx" data-language="nginx"><code class="language-nginx"><span class="token comment"># changer ça</span><span class="token directive"><span class="token keyword">listen</span> <span class="token number">443</span> ssl</span><span class="token punctuation">;</span><span class="token comment"># par ça :</span><span class="token directive"><span class="token keyword">listen</span> <span class="token number">443</span> ssl http2</span><span class="token punctuation">;</span></code></pre>]]></content>
    
    
    <summary type="html">Optimisez vos requêtes HTTPS du serveur Apache avec le protocole HTTP/2.</summary>
    
    
    
    
    <category term="http2" scheme="https://cogito-ergo-dev.fr/etiquettes/http2/"/>
    
    <category term="ssl" scheme="https://cogito-ergo-dev.fr/etiquettes/ssl/"/>
    
    <category term="performance" scheme="https://cogito-ergo-dev.fr/etiquettes/performance/"/>
    
  </entry>
  
  <entry>
    <title>CTE, générateur et récursivité avec SQLite</title>
    <link href="https://cogito-ergo-dev.fr/blog/31551/cte-generateur-et-recursivite-avec-sqlite/"/>
    <id>https://cogito-ergo-dev.fr/blog/31551/cte-generateur-et-recursivite-avec-sqlite/</id>
    <published>2021-01-17T22:24:29.000Z</published>
    <updated>2022-04-12T21:12:25.210Z</updated>
    
    <content type="html"><![CDATA[<p>Le SQL ne sert pas qu’à récupérer des données existantes, il peut aussi servirà créer des jeux de données plus ou moins compliqués en utilisant des générateurs.Les générateurs sont une façon d’utiliser les CTE pour créer des données. Nousallons voir comment, en partant d’exemples simples et en allant dans des cas pluscompliqués.</p><span id="more"></span><h2 id="Une-CTE-c’est-quoi"><a href="#Une-CTE-c’est-quoi" class="headerlink" title="Une CTE, c’est quoi ?"></a>Une CTE, c’est quoi ?</h2><p>Une CTE, ou <em lang="en">Common Table Expression</em> est un moyen en SQL d’isoleret de nommer une requête afin de la réutiliser immédiatement dans une autre requête.</p><p>Voyons par exemple cette CTE :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> very_simple_cte <span class="token keyword">AS</span> <span class="token punctuation">(</span>    <span class="token keyword">SELECT</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> very_simple_cte<span class="token punctuation">;</span></code></pre><p>C’est l’exact équivalent de cela :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> <span class="token punctuation">(</span><span class="token keyword">SELECT</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> very_simple_cte<span class="token punctuation">;</span></code></pre><p>Les CTE permettent donc, dans le cas des requêtes imbriquées complexes, d’ensimplifier l’écriture et la lecture.</p><p>Mais il n’y a pas que ça ! En effet, il est aussi possible d’avoir desCTE récursives !</p><h2 id="Et-maintenant-qu’est-ce-qu’une-CTE-recursive"><a href="#Et-maintenant-qu’est-ce-qu’une-CTE-recursive" class="headerlink" title="Et maintenant, qu’est-ce qu’une CTE récursive ?"></a>Et maintenant, qu’est-ce qu’une CTE récursive ?</h2><p>Imaginons une CTE assez simple, retournant 2 lignes. Nous pouvons d’abord l’envisager comme suit, avec un <code>UNION</code> en son sein :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> simple_cte<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token number">0</span>     <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>     <span class="token keyword">SELECT</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> simple_cte<span class="token punctuation">;</span></code></pre><p>Notez le <code>(x)</code>. Cela permet de nommer les champs retournés par la CTE.</p><p>Nous pouvons aussi faire 2 CTEs et les appeler ensuite en externalisant l’<code>UNION</code> :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> simple_cte_bis<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> simple_cte_bis<span class="token keyword">UNION</span> <span class="token keyword">ALL</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> simple_cte_bis<span class="token punctuation">;</span></code></pre><p>Bien. Ça peut vite devenir lourd si on a un nombre élevé de lignes devantrespecter un critère dans leur succession… Imaginons que nous souhaitonsgénérer <em>n</em> lignes…</p><p>C’est là qu’entre en jeux la récursivité des CTEs !</p><p>Pour cela, il suffit d’introduire, lors de la déclaration de la CTE, le mot clé<code>RECURSIVE</code>, mais aussi un garde fou, sinon on tourne en boucle sans fin. Disonsque nous arrêterons la suite au bout de 10 itérations.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE recursive_cte<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span>  <span class="token number">0</span>     <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>     <span class="token keyword">SELECT</span>  x    <span class="token keyword">FROM</span>    recursive_cte    <span class="token keyword">LIMIT</span>   <span class="token number">10</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> recursive_cte<span class="token punctuation">;</span></code></pre><ul><li>La première partie de l’<code>UNION</code> définit le point de départ, la première ligne.</li><li>La deuxième partie fait un <code>SELECT</code> simple mais… en appelant la CTE elle-même, et en sélectionnant le champ <code>x</code> déclaré au début.</li></ul><p>Ce petit bout de code SQL nous donne en sortie 10 lignes contenant un champ ayantzéro comme valeur.</p><p>Voilà, sans le savoir, vous venez de créer votre premier générateur. Basique,soit, mais un générateur tout de même, puisque parti de rien vous avez obtenu10 lignes en sortie.</p><h2 id="OK-Donc-une-CTE-recursive-c’est-un-generateur"><a href="#OK-Donc-une-CTE-recursive-c’est-un-generateur" class="headerlink" title="OK ! Donc, une CTE récursive, c’est un générateur ?"></a>OK ! Donc, une CTE récursive, c’est un générateur ?</h2><p>Oui, une CTE récursive est un moyen de créer un générateur. </p><p>Pratiquement, les générateurs peuvent servir à générer des suites de nombres, des distributions temporelles (mois, trimestres), ou des alphabets et même d’autres cas encore.</p><p>Voyons déjà le cas des générateurs de nombres avec quelques cas simples.</p><h2 id="Creons-des-generateurs-de-nombres"><a href="#Creons-des-generateurs-de-nombres" class="headerlink" title="Créons des générateurs de nombres !"></a>Créons des générateurs de nombres !</h2><p>Un premier cas simple est tout simplement l’incrémentation de nombres par 1. </p><p>Si nous voulons récupérer les nombres de 0 à 15, nous pouvons faire ainsi :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE generator<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token number">0</span>     <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>     <span class="token keyword">SELECT</span>  x <span class="token operator">+</span> <span class="token number">1</span>     <span class="token keyword">FROM</span>    generator     <span class="token keyword">WHERE</span>   x <span class="token operator">&lt;</span> <span class="token number">15</span> <span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> generator<span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">0123456789101112131415</code></pre><p>Mais nous pouvons également faire comme cela :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE generator<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token number">0</span>     <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>     <span class="token keyword">SELECT</span>  x <span class="token operator">+</span> <span class="token number">1</span>     <span class="token keyword">FROM</span>    generator     <span class="token keyword">LIMIT</span>   <span class="token number">16</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> generator<span class="token punctuation">;</span></code></pre><p>Dans chaque cas, la deuxième partie de l’<code>UNION</code> reprend la valeur précédenteet y ajoute 1.</p><p>Le garde-fou diffère : soit nous considérons une limitation par rapport à unevaleur du champs calculé, soit nous considérons une limite par rapport au nombrede résultats retournés.</p><p>L’utilisation de tel ou tel garde-fou dépendra de chaque cas.</p><p>De ce principe d’incrémentation simple, nous pouvons en déduire la conception de nombres avec un pas spécifique.</p><p>Mettons que nous souhaitons générer des nombres espacés de 5 à chaque fois, enne dépassant pas la valeur de 100 :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE generator_step<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token number">0</span>         <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>         <span class="token keyword">SELECT</span>  x <span class="token operator">+</span> <span class="token number">5</span>     <span class="token keyword">FROM</span>    generator_step     <span class="token keyword">WHERE</span>   x <span class="token operator">&lt;</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> generator_step<span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">05101520253035404550556065707580859095100</code></pre><p>Juste pour le fun, on double à chaque fois…</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE generator_double<span class="token punctuation">(</span>x<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token number">1</span>     <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>     <span class="token keyword">SELECT</span>  x <span class="token operator">*</span> <span class="token number">2</span>     <span class="token keyword">FROM</span>    generator_double     <span class="token keyword">LIMIT</span>   <span class="token number">30</span> <span class="token punctuation">)</span><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> generator_double<span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">124816326412825651210242048409681921638432768655361310722621445242881048576209715241943048388608167772163355443267108864134217728268435456536870912</code></pre><p>Je pense que vous avez compris le principe, beaucoup de suitesmathématiques sont réalisables ainsi. Voyez par exemple la suite de Fibonacci :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE generator_fibo <span class="token punctuation">(</span>p<span class="token punctuation">,</span> n<span class="token punctuation">)</span> <span class="token keyword">AS</span><span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token number">0</span><span class="token punctuation">,</span>            <span class="token number">1</span>     <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>          <span class="token keyword">SELECT</span> n<span class="token punctuation">,</span>            p <span class="token operator">+</span> n     <span class="token keyword">FROM</span>   generator_fibo     <span class="token keyword">WHERE</span>  n <span class="token operator">&lt;</span> <span class="token number">100</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span>  p<span class="token keyword">FROM</span>    generator_fibo<span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">01123581321345589</code></pre><p>Nous pouvons faire mumuse avec les nombres grâce aux générateurs. Faisons maintenant mumuse avec l’alphabet…</p><h2 id="Quid-des-generateurs-d’alphabet"><a href="#Quid-des-generateurs-d’alphabet" class="headerlink" title="Quid des générateurs d’alphabet ?"></a>Quid des générateurs d’alphabet ?</h2><p>Vu que les lettres peuvent être codées à partir de code <code>ASCII</code>, alors un générateur d’alphabet est tout aussi possible.</p><p>Voyons cela avec l’alphabet latin en générant des lettres majuscules :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE alphabet<span class="token punctuation">(</span>code<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token number">65</span>     <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>         <span class="token keyword">SELECT</span>  code <span class="token operator">+</span> <span class="token number">1</span>     <span class="token keyword">FROM</span>    alphabet     <span class="token keyword">WHERE</span>   code <span class="token operator">&lt;</span> <span class="token punctuation">(</span><span class="token number">65</span> <span class="token operator">+</span> <span class="token number">25</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token keyword">SELECT</span> <span class="token keyword">CHAR</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span> <span class="token keyword">FROM</span> alphabet<span class="token punctuation">;</span></code></pre><p>Le code <code>ASCII 65</code> est celui codant pour la lettre <code>A</code>. Les autres suivent dansl’ordre, donc un processus incrémental de un en un suffit. Au final on appliquela fonction <code>CHAR()</code> de SQLite pour avoir la lettre à partir du code.</p><h2 id="Cool-On-peut-faire-plus-complique-Avec-les-dates-par-exemple…"><a href="#Cool-On-peut-faire-plus-complique-Avec-les-dates-par-exemple…" class="headerlink" title="Cool ! On peut faire plus compliqué ? Avec les dates par exemple…"></a>Cool ! On peut faire plus compliqué ? Avec les dates par exemple…</h2><p>Oui ! Voyons deux cas pratiques : générer la liste des mois dans une année et la liste des trimestres, avec les bornes de début et de fin.</p><p>Commençons par la base : afficher les mois de l’année 1995 (pourquoi pas…).</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE by_month<span class="token punctuation">(</span>d<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token string">'1995-01-01'</span>     <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>         <span class="token keyword">SELECT</span>  <span class="token keyword">DATE</span><span class="token punctuation">(</span>d<span class="token punctuation">,</span> <span class="token string">'+1 MONTHS'</span><span class="token punctuation">)</span>     <span class="token keyword">FROM</span>    by_month     <span class="token keyword">WHERE</span>   d <span class="token operator">&lt;</span> <span class="token string">'1995-12-01'</span> <span class="token punctuation">)</span> <span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> by_month<span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">1995-01-011995-02-011995-03-011995-04-011995-05-011995-06-011995-07-011995-08-011995-09-011995-10-011995-11-011995-12-01</code></pre><p>Jusqu’ici, rien de bien compliqué. Notez l’utilisation de la fonction SQLite <code>DATE()</code>,qui permet de faire des incréments de durées, ici, 1 mois ajouté à la date à chaque fois.</p><p>Bien.</p><p>Maintenant, vous allez me dire : l’année en dur, c’est bête !</p><p>En effet, prenons donc en compte l’année courante :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE by_month<span class="token punctuation">(</span>d<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> strftime<span class="token punctuation">(</span><span class="token string">'%Y'</span><span class="token punctuation">,</span> <span class="token string">'now'</span><span class="token punctuation">)</span><span class="token operator">||</span><span class="token string">'-01-01'</span>         <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>         <span class="token keyword">SELECT</span>  <span class="token keyword">DATE</span><span class="token punctuation">(</span>d<span class="token punctuation">,</span> <span class="token string">'+1 MONTHS'</span><span class="token punctuation">)</span>     <span class="token keyword">FROM</span>    by_month     <span class="token keyword">WHERE</span>   d <span class="token operator">&lt;</span> <span class="token punctuation">(</span>strftime<span class="token punctuation">(</span><span class="token string">'%Y'</span><span class="token punctuation">,</span> <span class="token string">'now'</span><span class="token punctuation">)</span><span class="token operator">||</span><span class="token string">'-12-01'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> by_month<span class="token punctuation">;</span></code></pre><p>La fonction SQLite <code>STRFTIME()</code> permet de formater la date courante (<code>&#39;now&#39;</code>).Ici, on ne récupère que l’année sur 4 chiffres.</p><p>C’est bien, mais on peut faire mieux en ne répétant pas le <code>strftime(&#39;%Y&#39;, &#39;now&#39;)</code>.Pour cela, utilisons le fait qu’<strong>il est possible d’avoir plusieurs CTE enchaînées</strong>en définissant cet appel de fonction dans une CTE :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE const <span class="token keyword">AS</span> <span class="token punctuation">(</span>    <span class="token keyword">SELECT</span> strftime<span class="token punctuation">(</span><span class="token string">'%Y'</span><span class="token punctuation">,</span> <span class="token string">'now'</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> y<span class="token punctuation">)</span><span class="token punctuation">,</span>by_month<span class="token punctuation">(</span>d<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span>  const<span class="token punctuation">.</span>y<span class="token operator">||</span><span class="token string">'-01-01'</span>    <span class="token keyword">FROM</span>    const            <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>         <span class="token keyword">SELECT</span>  <span class="token keyword">DATE</span><span class="token punctuation">(</span>d<span class="token punctuation">,</span> <span class="token string">'+1 MONTHS'</span><span class="token punctuation">)</span>     <span class="token keyword">FROM</span>    by_month<span class="token punctuation">,</span> const    <span class="token keyword">WHERE</span>   d <span class="token operator">&lt;</span> <span class="token punctuation">(</span>const<span class="token punctuation">.</span>y<span class="token operator">||</span><span class="token string">'-12-01'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> by_month<span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">2021-01-012021-02-012021-03-012021-04-012021-05-012021-06-012021-07-012021-08-012021-09-012021-10-012021-11-012021-12-01</code></pre><p>Voilà, en ne se répétant pas, on évite les erreurs stupides.</p><p>La CTE <code>const</code> ne sort qu’une seule ligne contenant le champ <code>y</code> stockantl’année en cours sur 4 chiffres.</p><p>La CTE <code>by_month</code> utilise la CTE <code>const</code> pour avoir l’année qui est nécessaireà la construction des dates.</p><p>Ajoutons-y les bornes définissant les débuts et les fins de mois, avec encorenotre super fonction SQLite <code>DATE()</code> :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE const <span class="token keyword">AS</span> <span class="token punctuation">(</span>    <span class="token keyword">SELECT</span> strftime<span class="token punctuation">(</span><span class="token string">'%Y'</span><span class="token punctuation">,</span> <span class="token string">'now'</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> y<span class="token punctuation">)</span><span class="token punctuation">,</span>by_month<span class="token punctuation">(</span>d<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span>  const<span class="token punctuation">.</span>y<span class="token operator">||</span><span class="token string">'-01-01'</span>    <span class="token keyword">FROM</span>    const        <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>         <span class="token keyword">SELECT</span>  <span class="token keyword">DATE</span><span class="token punctuation">(</span>d<span class="token punctuation">,</span> <span class="token string">'+1 MONTHS'</span><span class="token punctuation">)</span>    <span class="token keyword">FROM</span>    by_month<span class="token punctuation">,</span> const    <span class="token keyword">WHERE</span>   d <span class="token operator">&lt;</span> <span class="token punctuation">(</span>const<span class="token punctuation">.</span>y<span class="token operator">||</span><span class="token string">'-12-01'</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token keyword">SELECT</span>  d <span class="token keyword">AS</span> date_start<span class="token punctuation">,</span>        <span class="token keyword">date</span><span class="token punctuation">(</span>            d<span class="token punctuation">,</span>            <span class="token string">'start of month'</span><span class="token punctuation">,</span>            <span class="token string">'+1 month'</span><span class="token punctuation">,</span>            <span class="token string">'-1 day'</span>        <span class="token punctuation">)</span> <span class="token keyword">AS</span> date_end<span class="token keyword">FROM</span>    by_month<span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">2021-01-01|2021-01-312021-02-01|2021-02-282021-03-01|2021-03-312021-04-01|2021-04-302021-05-01|2021-05-312021-06-01|2021-06-302021-07-01|2021-07-312021-08-01|2021-08-312021-09-01|2021-09-302021-10-01|2021-10-312021-11-01|2021-11-302021-12-01|2021-12-31</code></pre><p>Voilà ! Nous avons un moyen de générer une liste de chaque mois de l’annéecourante avec les dates de début et de fin de chaque mois.</p><p>De la même façon, la réalisation par trimestre en découle (encore merci la fonction <code>DATE()</code> !) :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE const <span class="token keyword">AS</span> <span class="token punctuation">(</span>    <span class="token keyword">SELECT</span> strftime<span class="token punctuation">(</span><span class="token string">'%Y'</span><span class="token punctuation">,</span> <span class="token string">'now'</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> y<span class="token punctuation">)</span><span class="token punctuation">,</span> by_trimester<span class="token punctuation">(</span>d<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span>  const<span class="token punctuation">.</span>y<span class="token operator">||</span><span class="token string">'-01-01'</span>     <span class="token keyword">FROM</span>    const        <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>         <span class="token keyword">SELECT</span>  <span class="token keyword">DATE</span><span class="token punctuation">(</span>d<span class="token punctuation">,</span> <span class="token string">'+3 MONTHS'</span><span class="token punctuation">)</span>     <span class="token keyword">FROM</span>    by_trimester     <span class="token keyword">LIMIT</span>   <span class="token number">4</span> <span class="token punctuation">)</span> <span class="token keyword">SELECT</span>  d <span class="token keyword">AS</span> date_start<span class="token punctuation">,</span>        <span class="token keyword">date</span><span class="token punctuation">(</span>            d<span class="token punctuation">,</span>            <span class="token string">'start of month'</span><span class="token punctuation">,</span>            <span class="token string">'+3 month'</span><span class="token punctuation">,</span>            <span class="token string">'-1 day'</span>        <span class="token punctuation">)</span> <span class="token keyword">AS</span> date_end<span class="token keyword">FROM</span>    by_trimester<span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">2021-01-01|2021-03-312021-04-01|2021-06-302021-07-01|2021-09-302021-10-01|2021-12-31</code></pre><p>Voilà ! Nos trimestres sont bien générés avec leurs bornes respectives.</p><h2 id="Notre-table-servant-d’exemple"><a href="#Notre-table-servant-d’exemple" class="headerlink" title="Notre table servant d’exemple"></a>Notre table servant d’exemple</h2><p>Avoir des générateurs, c’est bien, les utiliser dans quelques cas concrets, c’est mieux.</p><p>Notre exemple se basera sur une table comportant des commentaires laissés pardes internautes sur un site.</p><p>Un commentaire peut avoir un commentaire parent (dans ce cas, c’est une réponse)sans limitation dans la profondeur de la hiérarchie.</p><p>Le login de l’utilisateur, comportant que des caractères ASCII et commençantobligatoirement par une lettre, est enregistré.</p><p>La date de création du commentaire est enregistrée.</p><p>Voici la table en question :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> comments <span class="token punctuation">(</span>    id          <span class="token keyword">INTEGER</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span> <span class="token keyword">UNIQUE</span><span class="token punctuation">,</span>    parent_id   <span class="token keyword">INTEGER</span> <span class="token keyword">REFERENCES</span> comments<span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">,</span>    author      <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span>                <span class="token keyword">CHECK</span><span class="token punctuation">(</span>author <span class="token operator">&lt;></span> <span class="token string">''</span> <span class="token operator">AND</span> LENGTH<span class="token punctuation">(</span>TRIM<span class="token punctuation">(</span>author<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">,</span>    content     <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span>                <span class="token keyword">DEFAULT</span> <span class="token string">''</span><span class="token punctuation">,</span>    created_at  <span class="token keyword">DATETIME</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span>                <span class="token keyword">DEFAULT</span> <span class="token punctuation">(</span><span class="token keyword">datetime</span><span class="token punctuation">(</span><span class="token string">'now'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>On y injecte tout un tas de données, que vous retrouverez dans le <a href="source.sql">fichier source</a>.</p><h2 id="Lister-le-nombre-de-commentaires-par-trimestre"><a href="#Lister-le-nombre-de-commentaires-par-trimestre" class="headerlink" title="Lister le nombre de commentaires par trimestre"></a>Lister le nombre de commentaires par trimestre</h2><p>Au premier abord, vous pouvez me dire : « Réalisons juste des <code>GROUP BY</code> et basta ! ».Oui mais non. L’idée est de faire comme un rapport, donc s’il n’y a rien pourune période donnée, le <code>GROUP BY</code> ne le montrera pas. Par contre, si on utilise en plus les CTE vues précédemment pour les mois, alors là ça devient intéressant : il est possible de voir, si, pour un mois, un trimestre, il n’y a rien eu.</p><p>Pour notre exemple, nous allons lister le nombre de commentaires laissés pourchaque trimestre. Je vous livre l’exemple tel quel :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE by_trimester<span class="token punctuation">(</span>d<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token string">'2021-01-01'</span>         <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>         <span class="token keyword">SELECT</span>  <span class="token keyword">DATE</span><span class="token punctuation">(</span>d<span class="token punctuation">,</span> <span class="token string">'+3 MONTHS'</span><span class="token punctuation">)</span>     <span class="token keyword">FROM</span>    by_trimester     <span class="token keyword">LIMIT</span>   <span class="token number">4</span> <span class="token punctuation">)</span><span class="token punctuation">,</span>comments_by_date <span class="token keyword">AS</span> <span class="token punctuation">(</span>    <span class="token keyword">SELECT</span>      created_at    <span class="token keyword">FROM</span>        comments <span class="token punctuation">)</span><span class="token punctuation">,</span>borned_trimester <span class="token keyword">AS</span> <span class="token punctuation">(</span>    <span class="token keyword">SELECT</span>  d <span class="token keyword">AS</span> date_start<span class="token punctuation">,</span>            <span class="token keyword">date</span><span class="token punctuation">(</span>                d<span class="token punctuation">,</span>                <span class="token string">'start of month'</span><span class="token punctuation">,</span>                <span class="token string">'+3 month'</span><span class="token punctuation">,</span>                <span class="token string">'-1 day'</span>            <span class="token punctuation">)</span> <span class="token keyword">AS</span> date_end    <span class="token keyword">FROM</span>    by_trimester<span class="token punctuation">)</span><span class="token keyword">SELECT</span>  t<span class="token punctuation">.</span>date_start<span class="token punctuation">,</span>        t<span class="token punctuation">.</span>date_end<span class="token punctuation">,</span>        <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> amount<span class="token keyword">FROM</span>    comments_by_date <span class="token keyword">AS</span> c<span class="token punctuation">,</span>        borned_trimester <span class="token keyword">AS</span> t<span class="token keyword">WHERE</span>   strftime<span class="token punctuation">(</span><span class="token string">'%Y-%m-%d'</span><span class="token punctuation">,</span> c<span class="token punctuation">.</span>created_at<span class="token punctuation">)</span> <span class="token operator">>=</span> t<span class="token punctuation">.</span>date_start        <span class="token operator">AND</span>        strftime<span class="token punctuation">(</span><span class="token string">'%Y-%m-%d'</span><span class="token punctuation">,</span> c<span class="token punctuation">.</span>created_at<span class="token punctuation">)</span> <span class="token operator">&lt;=</span> t<span class="token punctuation">.</span>date_end<span class="token keyword">GROUP</span> <span class="token keyword">BY</span> t<span class="token punctuation">.</span>date_start<span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">2021-01-01|2021-03-31|172021-04-01|2021-06-30|42021-07-01|2021-09-30|42021-10-01|2021-12-31|10</code></pre><p>Vous reconnaissez les différentes parties ? La CTE des trimestres est prise tellequelle, ainsi que sa partie des bornes, maintenant dans sa propre CTE (<code>orned_trimester</code>).La CTE <code>comments_by_date</code> récupère juste les dates de création des commentaires.</p><p>La requête finale utilisant ces trois CTE est assez simple : </p><ul><li>Elle reprend les date de début et de fin de chaque trimestre pour les deux premiers champs.</li><li>Elle compte le nombre d’éléments pour chaque trimestre.</li><li>Le partie <code>WHERE</code> détermine les dates des commentaires devant se trouver dans tel ou tel trimestre.</li><li>Le <code>GROUP BY</code> compacte tout par trimestre, et permet ainsi le calcul du <code>COUNT</code> dans le <code>SELECT</code> pour avoir le nombre de commentaires.</li></ul><h2 id="Lister-le-nombre-d’identifiants-par-ordre-alphabetique"><a href="#Lister-le-nombre-d’identifiants-par-ordre-alphabetique" class="headerlink" title="Lister le nombre d’identifiants par ordre alphabétique"></a>Lister le nombre d’identifiants par ordre alphabétique</h2><p>Imaginons que, pour des besoins en interne, nous devons lister, pour chaquelettre de l’alphabet, le nombre de commentaires correspondant au login commençantpar cette lettre.</p><p>Sans surprise, nous allons reprendre notre CTE <code>alphabet</code> vue plus haut.</p><p>À celle-ci, nous ajoutons un autre CTE, qui va récupérer la première lettre dechaque login (via la fonction <code>SUBSTR()</code>) et la passer en majucule (via lafonction<code>UPPER()</code>), et, dans un autre champ, y placer lenombre de messages qui y correspondent :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span>  UPPER<span class="token punctuation">(</span>SUBSTR<span class="token punctuation">(</span>author<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> letter<span class="token punctuation">,</span>        <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> amount<span class="token keyword">FROM</span>    comments<span class="token keyword">GROUP</span> <span class="token keyword">BY</span> UPPER<span class="token punctuation">(</span>SUBSTR<span class="token punctuation">(</span>author<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Et enfin, une requête exploite ces deux CTE pour afficher l’alphabet avec pourchaque lettre le nombre de commentaires.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE alphabet<span class="token punctuation">(</span>code<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>     <span class="token keyword">SELECT</span> <span class="token number">65</span>         <span class="token keyword">UNION</span> <span class="token keyword">ALL</span>     <span class="token keyword">SELECT</span>  code <span class="token operator">+</span> <span class="token number">1</span>     <span class="token keyword">FROM</span>    alphabet     <span class="token keyword">WHERE</span>   code <span class="token operator">&lt;</span> <span class="token punctuation">(</span><span class="token number">65</span> <span class="token operator">+</span> <span class="token number">25</span><span class="token punctuation">)</span> <span class="token punctuation">)</span><span class="token punctuation">,</span>by_authors <span class="token punctuation">(</span>letter<span class="token punctuation">,</span> amount<span class="token punctuation">)</span> <span class="token keyword">AS</span> <span class="token punctuation">(</span>    <span class="token keyword">SELECT</span>  UPPER<span class="token punctuation">(</span>SUBSTR<span class="token punctuation">(</span>author<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> letter<span class="token punctuation">,</span>            <span class="token function">COUNT</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> amount    <span class="token keyword">FROM</span>    comments    <span class="token keyword">GROUP</span> <span class="token keyword">BY</span> UPPER<span class="token punctuation">(</span>SUBSTR<span class="token punctuation">(</span>author<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span><span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token keyword">SELECT</span>      <span class="token keyword">CHAR</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span> <span class="token keyword">AS</span> letter<span class="token punctuation">,</span>            <span class="token keyword">COALESCE</span><span class="token punctuation">(</span>amount<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">AS</span> comments<span class="token keyword">FROM</span>        alphabet <span class="token keyword">AS</span> a<span class="token keyword">LEFT</span> <span class="token keyword">JOIN</span>   by_authors <span class="token keyword">AS</span> b <span class="token keyword">ON</span> b<span class="token punctuation">.</span>letter <span class="token operator">=</span> <span class="token keyword">CHAR</span><span class="token punctuation">(</span>a<span class="token punctuation">.</span>code<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">A|0B|5C|7D|0E|0F|0G|0H|0I|0J|0K|0L|6M|0N|0O|0P|0Q|6R|1S|9T|0U|0V|1W|0X|0Y|0Z|0</code></pre><p>Ainsi, on peut voir que plein de logins ne déposent jamais de message…</p><h2 id="Arbre-des-reponses"><a href="#Arbre-des-reponses" class="headerlink" title="Arbre des réponses"></a>Arbre des réponses</h2><p>Et si, pour chaque <code>ID</code> de commentaire, on afficher son cheminement jusqu’à luiavec le contenu des commentaires parents ?</p><p>Pour cela, nous allons nous inspirer de l’article « <a href="/blog/30544/chemin-d-un-noeud-dans-un-arbre-hierarchique-de-donnees-avec-postgresql/" title="Chemin d’un nœud dans un arbre hiérarchique de données avec PostgreSQL">Chemin d’un nœud dans un arbre hiérarchique de données avec PostgreSQL</a> ».</p><p>Nous définissons donc une CTE <code>parents</code> contenant l’<code>ID</code>, le nombre de parents en cours et enfin le contenu.</p><p>Un premier jet peut être ceci :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">WITH</span> RECURSIVE parents <span class="token keyword">AS</span> <span class="token punctuation">(</span>    <span class="token keyword">SELECT</span>  id                          <span class="token keyword">AS</span> id<span class="token punctuation">,</span>            <span class="token number">0</span>                           <span class="token keyword">AS</span> ancestors<span class="token punctuation">,</span>            content                     <span class="token keyword">AS</span> tree    <span class="token keyword">FROM</span>    comments    <span class="token keyword">WHERE</span>   parent_id <span class="token operator">IS</span> <span class="token boolean">NULL</span>    <span class="token keyword">UNION</span>        <span class="token keyword">SELECT</span>  child<span class="token punctuation">.</span>id            <span class="token keyword">AS</span> id<span class="token punctuation">,</span>            c<span class="token punctuation">.</span>ancestors <span class="token operator">+</span> <span class="token number">1</span>     <span class="token keyword">AS</span> ancestors<span class="token punctuation">,</span>            <span class="token punctuation">(</span>                c<span class="token punctuation">.</span>tree                 <span class="token operator">||</span> <span class="token keyword">CHAR</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span>                <span class="token operator">||</span> child<span class="token punctuation">.</span>content            <span class="token punctuation">)</span> <span class="token keyword">AS</span> tree    <span class="token keyword">FROM</span> comments <span class="token keyword">as</span> child    <span class="token keyword">INNER</span> <span class="token keyword">JOIN</span> parents c <span class="token keyword">ON</span> c<span class="token punctuation">.</span>id <span class="token operator">=</span> child<span class="token punctuation">.</span>parent_id<span class="token punctuation">)</span><span class="token keyword">SELECT</span> id<span class="token punctuation">,</span> tree <span class="token keyword">FROM</span> parents<span class="token punctuation">;</span></code></pre><p>Ce qui donne une sortie… pas très sexy.</p><p>Ce qui serait bien, c’est :</p><ul><li>mettre le nom de l’auteur en première ligne, en majuscules</li><li>mettre le commentaire, tronqué.</li><li>décaler à chaque réponse d’un fil de discussion.</li><li>d’en faire une vue SQL :-)</li></ul><p>Allons-y ! Sortons l’arcenal de fonctions disponibles dans SQLite en natif :</p><ul><li><code>UPPER()</code> pour mettre les auteurs en majuscule</li><li><code>CHAR(10)</code> pour avoir un retour à la ligne</li><li><code>SUBSTR()</code> pour tronquer les chaînes de caractères</li><li><code>PRINTF()</code> pour créer une indentation du texte croissante</li></ul><p>Voici le code :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">VIEW</span> comments_tree <span class="token keyword">AS</span>    <span class="token keyword">WITH</span> RECURSIVE parents <span class="token keyword">AS</span> <span class="token punctuation">(</span>        <span class="token keyword">SELECT</span>  id                          <span class="token keyword">AS</span> id<span class="token punctuation">,</span>                <span class="token number">0</span>                           <span class="token keyword">AS</span> ancestors<span class="token punctuation">,</span>                UPPER<span class="token punctuation">(</span>author<span class="token punctuation">)</span><span class="token operator">||</span><span class="token string">':'</span><span class="token operator">||</span><span class="token keyword">CHAR</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span><span class="token operator">||</span>substr<span class="token punctuation">(</span>content<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span><span class="token number">50</span><span class="token punctuation">)</span><span class="token operator">||</span><span class="token string">'…'</span>  <span class="token keyword">AS</span> tree        <span class="token keyword">FROM</span>    comments        <span class="token keyword">WHERE</span>   parent_id <span class="token operator">IS</span> <span class="token boolean">NULL</span>        <span class="token keyword">UNION</span>                <span class="token keyword">SELECT</span>  child<span class="token punctuation">.</span>id            <span class="token keyword">AS</span> id<span class="token punctuation">,</span>                c<span class="token punctuation">.</span>ancestors <span class="token operator">+</span> <span class="token number">1</span>     <span class="token keyword">AS</span> ancestors<span class="token punctuation">,</span>                <span class="token punctuation">(</span>                    <span class="token keyword">char</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span> <span class="token operator">||</span> c<span class="token punctuation">.</span>tree                     <span class="token operator">||</span> <span class="token keyword">char</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span>                     <span class="token operator">||</span> printf<span class="token punctuation">(</span><span class="token string">"%"</span><span class="token operator">||</span><span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator">*</span> <span class="token punctuation">(</span>c<span class="token punctuation">.</span>ancestors <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string">"s"</span><span class="token punctuation">,</span> <span class="token string">' '</span><span class="token punctuation">)</span>                     <span class="token operator">||</span> UPPER<span class="token punctuation">(</span>child<span class="token punctuation">.</span>author<span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string">':'</span>                    <span class="token operator">||</span> <span class="token keyword">char</span><span class="token punctuation">(</span><span class="token number">10</span><span class="token punctuation">)</span>                     <span class="token operator">||</span> printf<span class="token punctuation">(</span><span class="token string">"%"</span><span class="token operator">||</span><span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator">*</span> <span class="token punctuation">(</span>c<span class="token punctuation">.</span>ancestors <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token operator">||</span> <span class="token string">"s"</span><span class="token punctuation">,</span> <span class="token string">' '</span><span class="token punctuation">)</span>                     <span class="token operator">||</span> substr<span class="token punctuation">(</span>child<span class="token punctuation">.</span>content<span class="token punctuation">,</span> <span class="token number">1</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token number">50</span> <span class="token operator">-</span> <span class="token punctuation">(</span><span class="token number">2</span> <span class="token operator">*</span> <span class="token punctuation">(</span>c<span class="token punctuation">.</span>ancestors <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span>                    <span class="token operator">||</span><span class="token string">'…'</span>                <span class="token punctuation">)</span> <span class="token keyword">AS</span> tree        <span class="token keyword">FROM</span> comments <span class="token keyword">as</span> child        <span class="token keyword">INNER</span> <span class="token keyword">JOIN</span> parents c <span class="token keyword">ON</span> c<span class="token punctuation">.</span>id <span class="token operator">=</span> child<span class="token punctuation">.</span>parent_id    <span class="token punctuation">)</span>    <span class="token keyword">SELECT</span> id<span class="token punctuation">,</span> tree <span class="token keyword">FROM</span> parents<span class="token punctuation">;</span></code></pre><p>Et voyons, par exemple pour l’<code>ID</code> 29, le fil de discussion :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> tree <span class="token keyword">FROM</span> comments_tree <span class="token keyword">WHERE</span> id <span class="token operator">=</span> <span class="token number">29</span><span class="token punctuation">;</span></code></pre><pre class="language-text" data-language="text"><code class="language-text">QUIDAM:Quo vadis?…  CAESAR:  Ego vadam Romam…    SPQR:    Quid??!!!…      SPQR:      Ut a sem ornare tellus faucibus tincidunt a …</code></pre><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>Les CTE, c’est pratique, il faut les utiliser sans modération :</p><ul><li>Elles améliorent la lisibilité des requêtes complexes.</li><li>Elles permettent de réaliser des générateurs de données !</li><li>La possibilité d’avoir des CTE récursives, outre le fait de créer des générateurs, permet d’avoir une souplesse et un contrôle supplémentaire sur les jeux de données.</li><li>La syntaxe est largement répendue dans les bases de données modernes.</li></ul><p>Attention toutefois : je vous ai exposé ici le cas de SQLite, pour d’autres bases de données, il peut y avoir des <strong>moyens plus simples</strong> encore que les CTE pour générer des données.</p><p>Par exemple, sous PostgreSQL, la fonction <code>generate_series()</code> permet de générer du contenu.</p><p>L’ensemble des codes sources de cet article sont disponibles dans ce <a href="source.sql">fichier SQL</a>.</p>]]></content>
    
    
    <summary type="html">Découvrez comment générer des données avec SQLite et les utiliser dans vos requêtes.</summary>
    
    
    
    
    <category term="db" scheme="https://cogito-ergo-dev.fr/etiquettes/db/"/>
    
    <category term="sqlite" scheme="https://cogito-ergo-dev.fr/etiquettes/sqlite/"/>
    
    <category term="algorithme" scheme="https://cogito-ergo-dev.fr/etiquettes/algorithme/"/>
    
  </entry>
  
  <entry>
    <title>Gérer les traductions dans une base de données</title>
    <link href="https://cogito-ergo-dev.fr/blog/9956/gerer-les-traductions-dans-une-base-de-donnees/"/>
    <id>https://cogito-ergo-dev.fr/blog/9956/gerer-les-traductions-dans-une-base-de-donnees/</id>
    <published>2020-12-03T16:01:05.000Z</published>
    <updated>2022-04-12T21:12:25.226Z</updated>
    
    <content type="html"><![CDATA[<p>Je vous montre ma manière de gérer les traductions de contenus en base de données. Cette méthode est simple, extensible et verrouillée.</p><span id="more"></span><h2 id="Prerequis"><a href="#Prerequis" class="headerlink" title="Prérequis"></a>Prérequis</h2><p>J’utilise dans cet article des exemples utilisant <strong>PostgreSQL</strong>, mais les principes de base sont <strong>adaptables à toutes les bases de données relationnelles</strong>.</p><p>Je vous montre une version générique (et simplifiée), àadapter selon vos besoins.</p><h2 id="Notre-exemple"><a href="#Notre-exemple" class="headerlink" title="Notre exemple"></a>Notre exemple</h2><p>L’exemple utilisé concerne un blog fictif, utilisant les langues disponiblesstockées dans une table <code>languages</code> et stockant la référence des articles dans la table <code>posts</code>.La table <code>t_posts</code> entre en jeu pour tous les champs traduisibles.</p><h2 id="Grandes-lignes"><a href="#Grandes-lignes" class="headerlink" title="Grandes lignes"></a>Grandes lignes</h2><p>Le principe est simple : pour <strong>chaque entrée</strong> dans la table <code>posts</code>, il y a <strong>au moins une autre entrée</strong> dans la table <code>t_posts</code> comportant des champs traduits dans une langue.</p><p>Chaque entrée dans <code>t_posts</code> comporte une <strong>clé étrangère obligatoire</strong> vers la table <code>posts</code> et<strong>une autre également obligatoire</strong> vers la table <code>languages</code>, en tâchant de contrôler l’<strong>unicité de cette association de clés</strong>.</p><h3 id="La-table-stockant-les-langues-supportees"><a href="#La-table-stockant-les-langues-supportees" class="headerlink" title="La table stockant les langues supportées"></a>La table stockant les langues supportées</h3><p>Il est temps de déclarer notre table :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> languages<span class="token punctuation">(</span>    id      <span class="token keyword">SERIAL</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span>    code    <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">,</span>    name    <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span>    <span class="token keyword">UNIQUE</span><span class="token punctuation">(</span>code<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Remplissons cette table :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> languages<span class="token punctuation">(</span>code<span class="token punctuation">,</span> name<span class="token punctuation">)</span><span class="token keyword">VALUES</span>      <span class="token punctuation">(</span><span class="token string">'fr'</span><span class="token punctuation">,</span> <span class="token string">'Français'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>            <span class="token punctuation">(</span><span class="token string">'en'</span><span class="token punctuation">,</span> <span class="token string">'English'</span><span class="token punctuation">)</span><span class="token punctuation">,</span>            <span class="token punctuation">(</span><span class="token string">'de'</span><span class="token punctuation">,</span> <span class="token string">'Deutsch'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Vérifions son contenu :</p><pre class="language-text" data-language="text"><code class="language-text"> id | code |   name   ----+------+----------  1 | fr   | Français  2 | en   | English   3 | de   | Deutsch (3 rows)</code></pre><p>Parfait, les langues autorisées pour le site sont correctement en placecomme nous le souhaitions.</p><h3 id="La-table-racine-des-articles"><a href="#La-table-racine-des-articles" class="headerlink" title="La table racine des articles"></a>La table racine des articles</h3><p>Définissons maintenant la table stockant les données communes entre chaque versiond’un même article.</p><p>Nous allons être simple : nous utiliserons un champ pour le <em>soft delete</em> et unchamp pour la clé primaire. Et c’est tout !</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> posts<span class="token punctuation">(</span>    id          <span class="token keyword">SERIAL</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span>    deleted_at  TIMESTAMPTZ<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><h3 id="La-table-contenant-les-versions-dans-differentes-langues-des-articles"><a href="#La-table-contenant-les-versions-dans-differentes-langues-des-articles" class="headerlink" title="La table contenant les versions dans différentes langues des articles"></a>La table contenant les versions dans différentes langues des articles</h3><p>Cette table sera plus fournie, car elle devra :</p><ul><li>avoir une référence non nulle vers la table <code>posts</code></li><li>avoir une référence non nulle vers la table <code>languages</code></li><li>ne pas avoir de doublon sur l’ensemble des deux clés étrangères ci-dessus</li><li>chaque nouvelle entrée ne sera pas publiée par défaut (brouillon systématique)</li></ul><p>Concrétisons cela :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">TABLE</span> t_posts<span class="token punctuation">(</span>    id              <span class="token keyword">SERIAL</span> <span class="token keyword">PRIMARY</span> <span class="token keyword">KEY</span><span class="token punctuation">,</span>    post_id         <span class="token keyword">INTEGER</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">REFERENCES</span> posts<span class="token punctuation">(</span>id<span class="token punctuation">)</span><span class="token punctuation">,</span>    language_code   <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">REFERENCES</span> languages<span class="token punctuation">(</span>code<span class="token punctuation">)</span><span class="token punctuation">,</span>    title           <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span><span class="token punctuation">,</span>    content         <span class="token keyword">TEXT</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">DEFAULT</span> <span class="token string">''</span><span class="token punctuation">,</span>    is_published    <span class="token keyword">BOOLEAN</span> <span class="token operator">NOT</span> <span class="token boolean">NULL</span> <span class="token keyword">DEFAULT</span> <span class="token string">'f'</span><span class="token punctuation">,</span>    <span class="token keyword">UNIQUE</span> <span class="token punctuation">(</span>        post_id<span class="token punctuation">,</span>        language_code    <span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><h2 id="Le-probleme-des-entrees-fantomes…"><a href="#Le-probleme-des-entrees-fantomes…" class="headerlink" title="Le problème des entrées fantômes…"></a>Le problème des entrées fantômes…</h2><p>Notre façon de séparer les champs liés à une version et le reste, est que nousrisquons de <strong>créer des entrées fantômes</strong> dans la table <code>posts</code> sans jamais avoirde traductions.</p><p>Nous devons donc <strong>inclure une sécurité</strong> consistant à ne jamais ajouter d’entréedans la table <code>posts</code> sans avoir au moins une entrée créer en même temps dansla table <code>t_posts</code>.</p><p>Comment faire cela ?</p><p>Soit nous mettons un <em>trigger</em> créant, lors d’une insertion, une entrée dans la table<code>posts</code> si on ne fourni pas de <code>post_id </code>et balançant cette valeur en tant que<code>post_id</code> pour l’insertion.</p><p><strong>Avantage :</strong> se fait de façon transparente et est non bloquant.</p><p><strong>Inconvénient :</strong> favorise la création de doublons potentiels car on ne se réfère pas à une entrée de la table <code>posts</code>. Celan’empêche également pas la création d’entrées dans la table <code>posts</code> sans au moins un élément traduit dans <code>t_posts</code>.</p><p>C’est donc une mauvaise piste.</p><p>Soit nous nous facilitons la tâche avec la création de fonctions et un verrouillage empêchant l’ajout directe dans la table <code>posts</code> :</p><ul><li>une fonction pour la création d’un nouveau post dans une langue,</li><li>et une fonction pour l’ajout d’une traduction à un post existant.</li><li>Un trigger sur la table <code>posts</code> pour empêcher toute commande <code>INSERT</code> directement.</li></ul><h2 id="Fonction-pour-l’ajout-d’un-nouvel-article"><a href="#Fonction-pour-l’ajout-d’un-nouvel-article" class="headerlink" title="Fonction pour l’ajout d’un nouvel article"></a>Fonction pour l’ajout d’un nouvel article</h2><p>La fonction prendra le minimum nécessaire pour définir un nouvel article : le <strong>titre</strong> et le <strong>code langue</strong>.En retour, la fonction <strong>fournit la valeur de la clé primaire</strong> de la table <code>posts</code>, afin d’être pratique et de pouvoir enchaîner facilement sur une édition ou l’ajout d’une version dans une autre langue.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token operator">OR</span> <span class="token keyword">REPLACE</span> <span class="token keyword">FUNCTION</span> new_post<span class="token punctuation">(</span>    <span class="token operator">IN</span> arg_title            <span class="token keyword">TEXT</span><span class="token punctuation">,</span>    <span class="token operator">IN</span> arg_language_code    <span class="token keyword">TEXT</span><span class="token punctuation">)</span><span class="token keyword">RETURNS</span> <span class="token keyword">INTEGER</span> <span class="token keyword">AS</span> $$<span class="token keyword">DECLARE</span>    pid     <span class="token keyword">INTEGER</span><span class="token punctuation">;</span><span class="token keyword">BEGIN</span>    <span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> posts <span class="token punctuation">(</span>id<span class="token punctuation">)</span> <span class="token keyword">VALUES</span><span class="token punctuation">(</span><span class="token keyword">DEFAULT</span><span class="token punctuation">)</span> <span class="token keyword">RETURNING</span> id <span class="token keyword">INTO</span> pid<span class="token punctuation">;</span>     <span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> t_posts <span class="token punctuation">(</span>post_id<span class="token punctuation">,</span> title<span class="token punctuation">,</span> language_code<span class="token punctuation">)</span>    <span class="token keyword">VALUES</span>      <span class="token punctuation">(</span>pid<span class="token punctuation">,</span> TRIM<span class="token punctuation">(</span>arg_title<span class="token punctuation">)</span><span class="token punctuation">,</span> arg_language_code<span class="token punctuation">)</span><span class="token punctuation">;</span>    <span class="token keyword">RETURN</span> pid<span class="token punctuation">;</span><span class="token keyword">END</span><span class="token punctuation">;</span>$$<span class="token keyword">LANGUAGE</span> plpgsql<span class="token punctuation">;</span></code></pre><p>Voilà notre première fonction créée.</p><h2 id="Fonction-pour-ajouter-une-nouvelle-traduction-a-un-article-existant"><a href="#Fonction-pour-ajouter-une-nouvelle-traduction-a-un-article-existant" class="headerlink" title="Fonction pour ajouter une nouvelle traduction à un article existant"></a>Fonction pour ajouter une nouvelle traduction à un article existant</h2><p>Cette fonction ressemble beaucoup à la précédente, sauf qu’en arguments d’entrées il y en a un supplémentaire pour l’ID de l’entrée dans <code>posts</code> et la valeur de retour est celle de la nouvelle entrée dans la table <code>t_posts</code>, pour pouvoir passer à l’édition tout de suite après par exemple.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token operator">OR</span> <span class="token keyword">REPLACE</span> <span class="token keyword">FUNCTION</span> add_translation_to_post<span class="token punctuation">(</span>    <span class="token operator">IN</span> arg_post_id          <span class="token keyword">INTEGER</span><span class="token punctuation">,</span>    <span class="token operator">IN</span> arg_title            <span class="token keyword">TEXT</span><span class="token punctuation">,</span>    <span class="token operator">IN</span> arg_language_code    <span class="token keyword">TEXT</span><span class="token punctuation">)</span><span class="token keyword">RETURNS</span> <span class="token keyword">INTEGER</span> <span class="token keyword">AS</span> $$<span class="token keyword">DECLARE</span>    tpid     <span class="token keyword">INTEGER</span><span class="token punctuation">;</span><span class="token keyword">BEGIN</span>    <span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> t_posts <span class="token punctuation">(</span>post_id<span class="token punctuation">,</span> title<span class="token punctuation">,</span> language_code<span class="token punctuation">)</span>    <span class="token keyword">VALUES</span>      <span class="token punctuation">(</span>                    arg_post_id<span class="token punctuation">,</span>                    TRIM<span class="token punctuation">(</span>arg_title<span class="token punctuation">)</span><span class="token punctuation">,</span>                    arg_language_code                <span class="token punctuation">)</span>    <span class="token keyword">RETURNING</span> id <span class="token keyword">INTO</span> tpid<span class="token punctuation">;</span>    <span class="token keyword">RETURN</span> tpid<span class="token punctuation">;</span><span class="token keyword">END</span><span class="token punctuation">;</span>$$<span class="token keyword">LANGUAGE</span> plpgsql<span class="token punctuation">;</span></code></pre><p>Nous avons maintenant ce qu’il faut pour ajouter autant de traduction que nous le permet la table <code>languages</code>.</p><h2 id="Trigger-interdisant-l’ajout-direct"><a href="#Trigger-interdisant-l’ajout-direct" class="headerlink" title="Trigger interdisant l’ajout direct"></a>Trigger interdisant l’ajout direct</h2><p>Le <em>trigger</em> que nous allons ajouter et appliquer à la table <code>posts</code> lèvera une erreur quand nous tenterons de créerdirectement des entrées dans <code>posts</code> via la commande <code>INSERT</code> et non via la fonction <code>new_post</code>.</p><p>En faisant ainsi, il n’y aura pas d’entrées fantômes.</p><p>Cette astuce est disponible dans sa version d’origine sur <a href="https://stackoverflow.com/a/24267468/3014143">Stackoverflow</a>.</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">CREATE</span> <span class="token keyword">FUNCTION</span> prevent_natural_insert_on_posts<span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">RETURNS</span> <span class="token keyword">trigger</span> <span class="token keyword">AS</span> $$<span class="token keyword">DECLARE</span>    stack <span class="token keyword">TEXT</span><span class="token punctuation">;</span>    fn    <span class="token keyword">INTEGER</span><span class="token punctuation">;</span><span class="token keyword">BEGIN</span>    RAISE EXCEPTION <span class="token string">'secured'</span><span class="token punctuation">;</span>      EXCEPTION <span class="token keyword">WHEN</span> OTHERS <span class="token keyword">THEN</span>    <span class="token keyword">BEGIN</span>        GET STACKED DIAGNOSTICS stack <span class="token operator">=</span> PG_EXCEPTION_CONTEXT<span class="token punctuation">;</span>                fn :<span class="token operator">=</span> position<span class="token punctuation">(</span><span class="token string">'new_post'</span> <span class="token operator">in</span> stack<span class="token punctuation">)</span><span class="token punctuation">;</span>        <span class="token keyword">IF</span> <span class="token punctuation">(</span>fn <span class="token operator">&lt;=</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token keyword">THEN</span>            RAISE EXCEPTION <span class="token string">'Expecting insert from new_post function'</span>            <span class="token keyword">USING</span> HINT <span class="token operator">=</span> <span class="token string">'Use function to insert data'</span><span class="token punctuation">;</span>        <span class="token keyword">END</span> <span class="token keyword">IF</span><span class="token punctuation">;</span>                <span class="token keyword">RETURN</span> new<span class="token punctuation">;</span>    <span class="token keyword">END</span><span class="token punctuation">;</span><span class="token keyword">END</span> $$ <span class="token keyword">LANGUAGE</span> plpgsql<span class="token punctuation">;</span><span class="token keyword">CREATE</span> <span class="token keyword">TRIGGER</span> prevent_normal_insert BEFORE <span class="token keyword">INSERT</span> <span class="token keyword">ON</span> posts<span class="token keyword">FOR EACH ROW</span> <span class="token keyword">EXECUTE</span> <span class="token keyword">PROCEDURE</span> prevent_natural_insert_on_posts<span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><h2 id="Testons"><a href="#Testons" class="headerlink" title="Testons !"></a>Testons !</h2><p>Voyons l’ajout direct sur la table <code>posts</code>, qui doit retourner une erreur :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">INSERT</span> <span class="token keyword">INTO</span> posts <span class="token punctuation">(</span>id<span class="token punctuation">)</span> <span class="token keyword">VALUES</span> <span class="token punctuation">(</span><span class="token keyword">DEFAULT</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Nous obtenons bien un message d’erreur :</p><pre class="language-text" data-language="text"><code class="language-text">ERROR:  Expecting insert from new_post functionHINT:  Use function to insert dataCONTEXT:  PL/pgSQL function prevent_natural_insert_on_posts() line 15 at RAISE</code></pre><p>Et un <code>SELECT</code> montre bien que la table <code>posts</code> est toujours vide.</p><p>Tentons alors d’ajouter une version à un article qui n’existe pas :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> add_translation_to_post<span class="token punctuation">(</span><span class="token number">42</span><span class="token punctuation">,</span> <span class="token string">'It should fail!'</span><span class="token punctuation">,</span> <span class="token string">'en'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Et les contraintes ont bien été définies puisque nous obtenons cette erreur :</p><pre class="language-text" data-language="text"><code class="language-text">ERROR:  insert or update on table "t_posts" violates foreign key constraint "t_posts_post_id_fkey"DETAIL:  Key (post_id)=(42) is not present in table "posts".CONTEXT:  SQL statement "INSERT INTO t_posts (post_id, title, language_code)    VALUES      (                    arg_post_id,                    TRIM(arg_title),                    arg_language_code                )    RETURNING id"PL/pgSQL function add_translation_to_post(integer,text,text) line 5 at SQL statement</code></pre><p>Maintenant que nous avons fait le tour des contraintes bloquantes, essayons d’entrer du contenu sans erreur en créant un article et en y ajoutant deux autres versions :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> new_post<span class="token punctuation">(</span><span class="token string">'Vous sortez du secteur américain'</span><span class="token punctuation">,</span> <span class="token string">'fr'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Retourne <code>2</code> dans mon cas, réutilisons-le :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> add_translation_to_post<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">'You are living american sector'</span><span class="token punctuation">,</span> <span class="token string">'en'</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token keyword">SELECT</span> add_translation_to_post<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">'Sie verlassen den Amerikanischen Sektor'</span><span class="token punctuation">,</span> <span class="token string">'de'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Voyons cela :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> <span class="token operator">*</span> <span class="token keyword">FROM</span> t_posts<span class="token punctuation">;</span></code></pre><p>Nous donne :</p><pre class="language-text" data-language="text"><code class="language-text"> id | post_id | language_code |                  title                  | content | is_published ----+---------+---------------+-----------------------------------------+---------+--------------  2 |       2 | fr            | Vous sortez du secteur américain        |         | f  3 |       2 | en            | You are living american sector          |         | f  4 |       2 | de            | Sie verlassen den Amerikanischen Sektor |         | f(3 rows)</code></pre><p>Tentons de provoquer une erreur en ajoutant une version qui existe déjà :</p><pre class="language-sql" data-language="sql"><code class="language-sql"><span class="token keyword">SELECT</span> add_translation_to_post<span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token string">'Hey! I am already here!'</span><span class="token punctuation">,</span> <span class="token string">'en'</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>…et l’erreur est bien levée :</p><pre class="language-text" data-language="text"><code class="language-text">ERROR:  duplicate key value violates unique constraint "t_posts_post_id_language_code_key"DETAIL:  Key (post_id, language_code)=(2, en) already exists.CONTEXT:  SQL statement "INSERT INTO t_posts (post_id, title, language_code)    VALUES      (                    arg_post_id,                    TRIM(arg_title),                    arg_language_code                )    RETURNING id"PL/pgSQL function add_translation_to_post(integer,text,text) line 5 at SQL statement</code></pre><h2 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h2><p>J’espère que cette façon de gérer les traductions vous sera utile dans l’utilisation que vous faites de votre base de données.</p><p>Ce que je vous ai montré est une version simplifiée, généralement un peu plus de contraintes et de cas spéciaux sont présents en pratique. À vous d’adapter les fonctions et les contraintes en fonction de votre situation. Un petit exercice serait de généraliser le <code>trigger</code> empêchant l’insertion directe afin qu’elle fonctionne pour toutes les tables auxquelles elle seraient reliée.</p>]]></content>
    
    
    <summary type="html">&lt;p&gt;Je vous montre ma manière de gérer les traductions de contenus en base de données. Cette méthode est simple, extensible et verrouillée.&lt;/p&gt;</summary>
    
    
    
    
    <category term="db" scheme="https://cogito-ergo-dev.fr/etiquettes/db/"/>
    
    <category term="postgresql" scheme="https://cogito-ergo-dev.fr/etiquettes/postgresql/"/>
    
    <category term="traduction" scheme="https://cogito-ergo-dev.fr/etiquettes/traduction/"/>
    
    <category term="langue" scheme="https://cogito-ergo-dev.fr/etiquettes/langue/"/>
    
  </entry>
  
  <entry>
    <title>Un format d’image plus léger que le WEBP arrive !</title>
    <link href="https://cogito-ergo-dev.fr/blog/60615/un-format-image-plus-leger-que-le-webp-arrive/"/>
    <id>https://cogito-ergo-dev.fr/blog/60615/un-format-image-plus-leger-que-le-webp-arrive/</id>
    <published>2020-11-12T17:47:02.000Z</published>
    <updated>2022-04-12T21:12:25.242Z</updated>
    
    <content type="html"><![CDATA[<p>Après le WEBP, Google Chrome (et Opera) supporte un autre format d’image particulièrement performant : le AVIF, pour <span lang="en">AV1 Image File Format</span>. Voyons ce qu’il vaut par rapport au WEBP.</p><span id="more"></span><p>Cet article reprend le flambeau de <a href="/blog/16107/des-images-plus-legeres-avec-ou-sans-perte-grace-a-webp/" title="Des images plus légères avec ou sans perte grâce à Webp">Des images plus légères avec ou sans perte grâce à Webp</a>, dans le sens ou un nouveau format d’image perce en ce moment avec un niveau de compression encore jamais atteint. Ou presque.</p><h2 id="Exemples-concrets"><a href="#Exemples-concrets" class="headerlink" title="Exemples concrets"></a>Exemples concrets</h2><p>J’ai commencé à utiliser ce format sur différents projets, dont mon blog technique que vous lisez en ce moment même.</p><p>Plutôt qu’un long discours, voyons ce que son utilisation donne réellement avec ces différents projets.</p><h3 id="Le-blog"><a href="#Le-blog" class="headerlink" title="Le blog"></a>Le blog</h3><p>Les images de couverture des articles du blog que vous lisez actuellement :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">echo</span> -e <span class="token string">"JPEG :"</span> <span class="token variable"><span class="token variable">`</span><span class="token function">du</span> -cb source/_posts/*/cover.jpg <span class="token operator">|</span> <span class="token function">tail</span> -n <span class="token number">1</span><span class="token variable">`</span></span>  <span class="token operator">&amp;&amp;</span> <span class="token punctuation">\</span><span class="token builtin class-name">echo</span> -e <span class="token string">"WEBP :"</span> <span class="token variable"><span class="token variable">`</span><span class="token function">du</span> -cb source/_posts/*/cover.webp <span class="token operator">|</span> <span class="token function">tail</span> -n <span class="token number">1</span><span class="token variable">`</span></span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">\</span><span class="token builtin class-name">echo</span> -e <span class="token string">"AVIF :"</span> <span class="token variable"><span class="token variable">`</span><span class="token function">du</span> -cb source/_posts/*/cover.avif <span class="token operator">|</span> <span class="token function">tail</span> -n <span class="token number">1</span><span class="token variable">`</span></span></code></pre><p>Donne :</p><pre class="language-text" data-language="text"><code class="language-text">JPEG : 1840498 totalWEBP : 583496 totalAVIF : 467819 total</code></pre><p>Sur un total de 19 images en 800×400px pour chaque format.</p><ul><li>Ici, le format WEBP fait en moyenne 31,7 % la taille du JPEG d’origine. Soit un peu moins du tiers.</li><li>l’équivalent AVIF fait 25,4 % la taille de l’image originale. Soit un quart.</li></ul><h3 id="Projet-A"><a href="#Projet-A" class="headerlink" title="Projet A"></a>Projet A</h3><p>Pour un autre projet j’ai 42 images, carrées, de tailles variables pour chaque format avec ceci comme résultat :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">echo</span> -e <span class="token string">"JPEG :"</span> <span class="token variable"><span class="token variable">`</span><span class="token function">du</span> -cb *.jpg <span class="token operator">|</span> <span class="token function">tail</span> -n <span class="token number">1</span><span class="token variable">`</span></span>  <span class="token operator">&amp;&amp;</span> <span class="token punctuation">\</span><span class="token builtin class-name">echo</span> -e <span class="token string">"WEBP :"</span> <span class="token variable"><span class="token variable">`</span><span class="token function">du</span> -cb *.webp <span class="token operator">|</span> <span class="token function">tail</span> -n <span class="token number">1</span><span class="token variable">`</span></span> <span class="token operator">&amp;&amp;</span> <span class="token punctuation">\</span><span class="token builtin class-name">echo</span> -e <span class="token string">"AVIF :"</span> <span class="token variable"><span class="token variable">`</span><span class="token function">du</span> -cb *.avif <span class="token operator">|</span> <span class="token function">tail</span> -n <span class="token number">1</span><span class="token variable">`</span></span></code></pre><pre class="language-text" data-language="text"><code class="language-text">JPEG : 922002 totalWEBP : 299280 totalAVIF : 260945 total</code></pre><ul><li>Ici, le format WEBP fait en moyenne 32,46 % la taille du JPEG d’origine. Presque le tiers.</li><li>l’équivalent AVIF fait 28,30 % la taille de l’image originale. Plus que le quart.</li></ul><h3 id="Projet-B"><a href="#Projet-B" class="headerlink" title="Projet B"></a>Projet B</h3><p>Encore un autre projet, ayant 101 images dans chaque format :</p><pre class="language-text" data-language="text"><code class="language-text">JPEG : 9477198 totalWEBP : 2694428 totalAVIF : 2128490 total</code></pre><ul><li>Ici, le format WEBP fait en moyenne 28,43 % la taille du JPEG d’origine. Plus que le quart.</li><li>l’équivalent AVIF fait 22,45 % la taille de l’image originale. Moins que le quart.</li></ul><h2 id="Comment-je-genere-mes-fichiers-AVIF"><a href="#Comment-je-genere-mes-fichiers-AVIF" class="headerlink" title="Comment je génère mes fichiers AVIF ?"></a>Comment je génère mes fichiers AVIF ?</h2><p>Pour le moment, ImageMagick sur Ubuntu 18.04 ne supporte pas ce format. J’ai donc téléchargé <a href="https://github.com/Kagami/go-avif/releases">Go AVIF</a>, un petit utilitaire en ligne de commande écrit en Go convertissant des images au format AVIF. Les binaires téléchargeables sont disponibles pour les trois principaux systèmes d’exploitation : Windows, Mac et Linux. Pour les autres systèmes, téléchargez les sources et compilez-les.</p><p>Je vous fournis l’aide du programme <code>avif-linux-x64 --help</code> :</p><pre class="language-text" data-language="text"><code class="language-text">Usage: avif [options] -e src_filename -o dst_filenameAVIF encoderOptions:  -h, --help                Give this help  -V, --version             Display version number  -e &lt;src>, --encode=&lt;src>  Source filename  -o &lt;dst>, --output=&lt;dst>  Destination filename  -q &lt;qp>, --quality=&lt;qp>   Compression level (0..63), [default: 25]  -s &lt;spd>, --speed=&lt;spd>   Compression speed (0..8), [default: 4]  --lossless                Lossless compression (alias for -q 0)  --best                    Slowest compression method (alias for -s 0)  --fast                    Fastest compression method (alias for -s 8)</code></pre><p>Si vous ne savez pas quoi prendre comme option, contentez-vous juste de convertir avec les options <code>-e</code> et <code>-o</code> pour indiquer le fichier d’entrée et le fichier de sortie. Le résultat obtenu sera déjà très convenable.</p><p>Si vous ne voulez pas de perte, ajouter l’option <code>--lossless</code>, pour convertir un PNG par exemple.</p><p>Voici pour vous un script Bash très simple pour <strong>convertir des fichiers images par lot</strong>, à adapter à votre goût :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token shebang important">#!/bin/bash</span><span class="token keyword">for</span> <span class="token for-or-select variable">img</span> <span class="token keyword">in</span> *.<span class="token punctuation">&#123;</span>jpg,jpeg,png,gif<span class="token punctuation">&#125;</span><span class="token punctuation">;</span> <span class="token keyword">do</span>    <span class="token assign-left variable">filename</span><span class="token operator">=</span><span class="token variable">$&#123;img<span class="token operator">%</span>.*&#125;</span>    <span class="token keyword">if</span> <span class="token punctuation">[</span> -f <span class="token string">"<span class="token variable">$filename</span>.avif"</span> <span class="token punctuation">]</span>    <span class="token keyword">then</span>        <span class="token builtin class-name">echo</span> <span class="token string">"<span class="token variable">$filename</span>.avif existe déjà. Pas de conversion."</span>    <span class="token keyword">else</span>avif-linux-x64 -e <span class="token string">"<span class="token variable">$img</span>"</span> -o <span class="token string">"<span class="token variable">$filename</span>.avif"</span>    <span class="token keyword">fi</span><span class="token keyword">done</span></code></pre><p>Enregistrez-le dans le fichier nommé <code>to-avif</code> puis changez ses droits pour le rendre exécutable et placez-le au bon endroit :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token function">chmod</span> +x to-avif<span class="token function">sudo</span> <span class="token function">mv</span> to-avif /usr/local/bin/to-avif</code></pre><p>Placez-vous dans un dossier avec plein d’images à convertir et exécutez la nouvelle commande disponible :</p><pre class="language-bash" data-language="bash"><code class="language-bash"><span class="token builtin class-name">cd</span> /mon/dossier/plein/imagesto-avif</code></pre><p>Le programme met un peu de temps à tourner, il est très gourmand en ressource.</p><h2 id="Comment-le-format-AVIF-est-il-supporte-par-les-navigateurs"><a href="#Comment-le-format-AVIF-est-il-supporte-par-les-navigateurs" class="headerlink" title="Comment le format AVIF est-il supporté par les navigateurs ?"></a>Comment le format AVIF est-il supporté par les navigateurs ?</h2><p>À la date de rédaction de cet article, le 12 novembre 2020, le site <a href="https://caniuse.com/avif">Can I Use montre une très faible adoption par les navigateurs</a> de ce format. Mais, comme dit plus haut, <strong>Google Chrome</strong>, un des navigateurs les plus utilisés le supporte depuis <strong>la version 85</strong>.</p><p>Le navigateur <strong>Opera</strong> suit le choix de Google Chrome depuis <strong>la version 71</strong>.</p><p>Pour <strong>Firefox</strong>, il faut pour le moment <strong>activer un drapeau</strong> pour l’avoir depuis la version 77. C’est donc inexistant pour le grand public.</p><p>De toute manière, il vous faudra utiliser la balise <code>PICTURE</code> et les balises <code>SOURCE</code> pour avoir une image quel que soit le navigateur :</p><pre class="language-markup" data-language="markup"><code class="language-markup"><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>picture</span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/img/ma-belle-photo.avif<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/avif<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/img/ma-belle-photo.webp<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/webp<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>    <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/img/mabelle-photo.jpg<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>image/jpeg<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Mon texte de secours<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>picture</span><span class="token punctuation">></span></span></code></pre><h2 id="Pour-et-contre"><a href="#Pour-et-contre" class="headerlink" title="Pour et contre"></a>Pour et contre</h2><p>Petite note préliminaire : je n’ai utilisé pour le moment que <strong>Go Avif</strong>, il y a d’autres logiciels disponibles, je ne les ai tout simplement pas encore testés.</p><h3 id="Pour"><a href="#Pour" class="headerlink" title="Pour"></a>Pour</h3><ul><li>Le poids des images obtenues est du même ordre que celles obtenues avec le WEBP, en un peu plus léger.</li><li>Ce format est supporté par Google Chrome, le poids lourd du marché des navigateurs.</li><li>Il peut convertir sans perte si on le souhaite (comme WEBP).</li><li>Il peut convertir des images animées GIF par exemple (comme WEBP).</li><li>Il est possible de choisir le niveau de compression et de qualité (comme WEBP)</li><li>Il sera prochainement sur Firefox sans drapeau. Mais quand ?</li></ul><h3 id="Contre"><a href="#Contre" class="headerlink" title="Contre"></a>Contre</h3><ul><li>La différence n’est pas si énorme que cela avec le WEBP en utilisant les options par défaut.</li><li>Le convertisseur est très gourmand en ressource lors de la conversion (mais vraiment), alors que le convertisseur WEBP (via <strong>ImageMagick</strong> ou <strong>cwebp</strong>) est particulièrement véloce. Pensez-y si vous le mettez sur un serveur web…</li><li>Le convertisseur plante sur des images ayant une trop grande taille.</li><li>Impossible d’afficher l’image AVIF dans Google Chrome hors du contexte d’une page web. Un lien direct vers une image AVIF n’affichera pas l’image, mais forcera son téléchargement.</li></ul><h2 id="Mon-choix"><a href="#Mon-choix" class="headerlink" title="Mon choix"></a>Mon choix</h2><p>Je l’utilise de <strong>manière expérimentale pour le moment</strong>, sur des projets ayant des images de petites à moyennes tailles, et dont on en maîtrise l’ajout (fiabilité et charge processeur me pousse à être très sage). <strong>Idéalement, sur un site statique, c’est parfait</strong>. La preuve en est avec ce site.</p><p>Pour le moment, si vous avez des photos dont vous souhaitez en réduire le poids numérique, <strong>alors restez seulement sur du WEBP</strong>. <a href="https://caniuse.com/webp">Son support est largement répandu sur les navigateurs</a>, les convertisseurs tournent <strong>bien et vite</strong>, et le poids obtenu est presque équivalent. Ce sera toujours <strong>mille fois mieux que du PNG ou du JPEG</strong> de base.</p><p>Bref, il faut que le support du format AVIF devienne plus mâture et plus répandu. Considérer-le expérimentalement ou dans des environnements parfaitement maîtrisés.</p>]]></content>
    
    
    <summary type="html">Le format d’image AVIF arrive et concurrence le format WEBP. Faut-il l’utiliser dès maintenant ?</summary>
    
    
    
    
    <category term="image" scheme="https://cogito-ergo-dev.fr/etiquettes/image/"/>
    
    <category term="cli" scheme="https://cogito-ergo-dev.fr/etiquettes/cli/"/>
    
    <category term="webp" scheme="https://cogito-ergo-dev.fr/etiquettes/webp/"/>
    
    <category term="avif" scheme="https://cogito-ergo-dev.fr/etiquettes/avif/"/>
    
  </entry>
  
</feed>
