Brico-Info - le blog de Bruno CATTEAU

Aller au contenu | Aller au menu | Aller à la recherche

mercredi 24 janvier 2007

Cohabitation de ROWNUM et ORDER BY sous ORACLE

Dans le cadre d'une gestion en liste paginée et triable, on est tenté d'utiliser la fonctionnalité ROWNUM  pour la pagination (c'est à dire les n premiers résultats) et le ORDER BY pour le tri, sauf que sous ORACLE, le ORDER BY s'applique après le ROWNUM, donc si vous trier par nom les 25 premiers résultats d'une liste de 1000 noms, vous aurez en première page, les 25 premiers résultats de la requete non trié (c'est à dire un échantillon de A à Z) trié par nom...

Pour éviter ça, il faut encapsuler la première requète dans une deuxième :

SELECT * FROM
   (SELECT champs FROM table ORDER BY champ_a_trier)
WHERE ROWNUM < 25;

Sources : http://archives.postgresql.org/pgsql-general/2001-07/msg01243.php

jeudi 21 décembre 2006

Problème de date dans une base de données

Une autre problématique dès que l'on travaille sur plusieurs bases (Access, Sql Server, Oracle) et que l'on est confronté à des langues différentes au niveau des serveurs... Même le LCID = 1036 (qui permet d'imposer la langue utilisée par le serveur web) ne vient pas à bout de certaines architectures ! En plus la syntaxe SQL de la langue n'est pas identique selon les bases. Donc pour éviter de voir le 7 mai se transformer en 5 juillet, voilà une solution, l'utilisation du format ODBC compatible avec les trois bases citées :

L'idée est de transformer un format de date jj/mm/aaaa ou mm/dd/yyyy en un format identique : {d 'aaaa-mm-jj} {d 'yyyy-mm-dd} ou un format comportant les heures : {ts 'yyyy-mm-dd hh:mm:ss'} ou {ts 'aaaa-mm-jj hh:mm:ss'}

La requête SQL devient  "... WHERE champ > {d '2006-08-23} "


'****************************************************************************************************
' Fonction     : strODBCDate
' Objet        : Preparer l'insertion d'une date dans une requete SQL
' Entrée       :
'               - dtmParam : date a traiter
' Retour       : chaine a inserer directement dans le code SQL
' Sortie       :
' Commentaires : le format final est {d 'aaaa-mm-jj'}
'                quelle que soit la langue d'installation de la base cible
' Exemple      :
'****************************************************************************************************
Function strODBCDate(ByVal dtmParam)
 Dim strOut
 If IsNull(dtmParam) Or dtmParam="" Then
  strOut = Null
 Else
  If Not IsDate(dtmParam) Then
   strOut = Null
  Else
   Dim strMonth, strDay
   Dim dtmMonth, dtmDay
   ' Traitement du mois si sur un seul chiffre
   dtmMonth = Month(dtmParam)
   If dtmMonth<10 Then
    strMonth = "0" & CStr(dtmMonth)
   Else
    strMonth = CStr(dtmMonth)
   End If
   ' Traitement du jour si sur un seul chiffre
   dtmDay = Day(dtmParam)
   If dtmDay<10 Then
    strDay = "0" & CStr(dtmDay)
   Else
    strDay = CStr(dtmDay)
   End If
   ' Creation de la chaine de retour
   strOut = "{d '" & CStr(Year(dtmParam)) & "-" & strMonth & "-" & strDay & "'}"
  End If
 End If
 strODBCDate = strOut
End Function


'=======================================================================================
' Fonction     : strODBCDateTime
' Objet        : Preparer l'insertion d'une date dans une requete SQL
' Entrée       :
'               - dtmParam : date et heure a traiter
' Retour       : chaine a inserer directement dans le code SQL
' Sortie       :
' Commentaires : le format final est {ts 'aaaa-mm-jj hh:mm:ss'}
'                quelle que soit la langue d'installation de la base cible
' Exemple      :
'=======================================================================================
Function strODBCDateTime(ByVal dtmParam)
 Dim strOut
 If IsNull(dtmParam) Or dtmParam="" Then
  strOut = Null
 Else
  If Not IsDate(dtmParam) Then
   strOut = Null
  Else
   Dim strMonth, strDay
   Dim dtmMonth, dtmDay
   Dim strHour, strMinutes, strSecondes
   strHour =hour(dtmParam)
   strMinutes= minute(dtmParam)
   strSecondes = second(dtmParam)
   ' Traitement du mois si sur un seul chiffre
   dtmMonth = Month(dtmParam)
   If dtmMonth<10 Then
    strMonth = "0" & CStr(dtmMonth)
   Else
    strMonth = CStr(dtmMonth)
   End If
   ' Traitement du jour si sur un seul chiffre
   dtmDay = Day(dtmParam)
   If dtmDay<10 Then
    strDay = "0" & CStr(dtmDay)
   Else
    strDay = CStr(dtmDay)
   End If
   If strHour<10 Then
    strHour = "0" & CStr(strHour)
   Else
    strHour = CStr(strHour)
   End If
   If strMinutes<10 Then
    strMinutes = "0" & CStr(strMinutes)
   Else
    strMinutes = CStr(strMinutes)
   End If
   If strSecondes<10 Then
    strSecondes = "0" & CStr(strSecondes)
   Else
    strSecondes = CStr(strSecondes)
   End If
   ' Creation de la chaine de retour
   strOut = "{ts '" & CStr(Year(dtmParam)) & "-" & strMonth & "-" & strDay & " "& strHour & ":"& strMinutes & ":"& strSecondes&"'}"
  End If
 End If
 strODBCDateTime = strOut
End Function 


 

mercredi 20 décembre 2006

Oracle : Mise à jour des sequences après import

Ames sensibles, s'abstenir...

Voilà un sujet qui est abordé sur internet sans forcement apporter de solution. Voici donc un script qui règle le problème.

Problème :

Sous Oracle, les séquences gèrent les "numéro auto" de façon indépendante. Si aucun trigger n'est mis en place, il faut appeler manuellement la valeur suivante de l'id au moment de faire une insertion. Mais dans le cadre d'un import de données brutes, les sequences ne sont pas mises à jour. La valeur "START" de la séquence ne peut être mise à jour...

Solution :

Il faut donc détruire les séquences et les recréer en prenant comme valeur de début, le MAX de l'id de la table en question + 1.

Pour la destruction de l'ensemble des séquences, voilà le script :

begin
  for i in (select sequence_name from user_sequences ) loop
    execute immediate 'drop sequence '||i.sequence_name;
  end loop;
end;
/

Puis pour la création :

DECLARE
 PROCEDURE CREATE_SEQ (table_name IN VARCHAR2, seq_name IN VARCHAR2, column_name IN VARCHAR2, nb_cache IN INTEGER)
 IS 
  cursor_handle INTEGER;
  seq_sql VARCHAR2(400);
  resu INTEGER;
  seq_start INTEGER;
 BEGIN
  
  seq_sql := 'SELECT MAX(' || column_name || ') FROM ' || table_name;  
    
  cursor_handle := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(cursor_handle,seq_sql,DBMS_SQL.V7);
  DBMS_SQL.DEFINE_COLUMN (cursor_handle,1,seq_start);
  resu:= DBMS_SQL.EXECUTE (cursor_handle);
  
  IF DBMS_SQL.FETCH_ROWS (cursor_handle) = 0
  THEN
   seq_start:=1;
  ELSE
   DBMS_SQL.COLUMN_VALUE(cursor_handle,1,seq_start);
   IF seq_start IS NULL
   THEN
    seq_start:=1;
   ELSE 
    seq_start:=seq_start + 1;
   END IF; 
  END IF; 
        
  DBMS_SQL.CLOSE_CURSOR (cursor_handle);
  
  seq_sql := 'CREATE SEQUENCE ' || SUBSTR(seq_name,1,26) || ' START WITH '
    || TO_CHAR(seq_start) || ' INCREMENT BY 1 CACHE ' || TO_CHAR(nb_cache);
  cursor_handle := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(cursor_handle,seq_sql,DBMS_SQL.V7);
  resu:= DBMS_SQL.EXECUTE (cursor_handle);
  DBMS_SQL.CLOSE_CURSOR (cursor_handle);
  
  DBMS_OUTPUT.PUT_LINE('Sequence ' || table_name || ' créée.');
 END;
 
BEGIN
 DBMS_OUTPUT.ENABLE(10000);
 CREATE_SEQ ('table', 'sequence','id', 5);
 CREATE_SEQ ('table2', 'sequence2','id2', 5);
 
END;
/
 

Mots clé anglais sur le sujet :

"Oracle update sequence" "restart sequence max value"

dimanche 10 décembre 2006

Recherche "Case insensitive", "Accent insensitive" multi-base

Pour faire une recherche "Case insensitive" et "Accent insensitive", plusieurs solutions :

  • Configurer la table de la base "CIAI" et chaque requete sera  "Case Insensitive" et "Accent Insensitive"
  • Transformer le mot recherché pour parametrer la requetes SQL permettant de comparer des choses comparables...

Sous ORACLE :

SELECT * from table WHERE TRANSLATE (UPPER(champ4), 'ÉÈÊÀÁÂÄÇÌÍÎÏÑÓÒÔÖÚÙÛÜ', 'EEEAAAACIIIINOOOOUUUU') LIKE TRANSLATE (UPPER('%réunion  commerciale%'), 'ÉÈÊÀÁÂÄÇÌÍÎÏÑÓÒÔÖÚÙÛÜ', 'EEEAAAACIIIINOOOOUUUU')
Cette syntaxe permet de comparer des chaines en majuscule dont les caractères accentués ont été remplacé par des caractères non accentués...).

Sous Oracle, il y a une autre solution (merci Cédric Courlet)

UPPER(CONVERT(VAR_SEARCHSTRING, 'US7ASCII'))

Sous SQL Server et Access :

SELECT * from table WHERE champ4 like '%r[eéèêë][uúùûü][nñ][iìíîï][oóòôö]n%'
Dans ce cas, on voit que le SQL gère une forme de syntaxe d'expression régulière alors que sous ORACLE, on a du procéder à un remplacement.

Dans ces deux cas, on a accès à une recherche riche puisque "réunion commerciale" ressort les éléments suivants :

  • En majuscule sans accent : REUNION COMMERCIALE
  • En minuscule sans accents : reunion commerciale
  • En majuscule avec accent : RÉUNION COMMERCIALE
  • En minuscule sans accent : réunion commerciale

Tout quoi !

Concaténation : Compatibilité inter-base

Le caractère permettant de concaténer deux champs dans une requête sont différents sous Access, Sql Server et Oracle !

  • Pour MS Access ce sera "&"
  • Pour MS Sql Server "+"
  • Pour Oracle "||"

Exemple :

SELECT champ1 & champ2 FROM table WHERE clause
SELECT champ1 + champ2 FROM table WHERE clause
SELECT champ1 || champ2 FROM table WHERE clause

Allez comprendre !

- page 1 de 2