Sunday, October 28, 2012

How to suppress output of duplicate values - Oracle SQL

SQL> select deptno, ename
  2  from emp
  3  order by deptno
  4  ;

    DEPTNO ENAME                                                                
---------- ----------                                                           
        10 CLARK                                                                
        10 KING                                                                 
        10 MILLER                                                               
        20 JONES                                                                
        20 FORD                                                                 
        20 ADAMS                                                                
        20 SMITH                                                            
        20 SCOTT                                                                
        30 WARD                                                                 
        30 TURNER                                                               
        30 ALLEN                                                                
        30 JAMES                                                           
        30 BLAKE                                                                
        30 MARTIN                                                               

14 rows selected.

SQL> -- We want to produce report with only new values of DEPTNO column,
SQL> -- without repetition. SQL*Plus provides BREAK ON feature.

SQL> break on deptno
SQL> select deptno, ename
  2  from emp
  3  order by deptno
  4  ;

    DEPTNO ENAME                                                                
---------- ----------                                                           
        10 CLARK                                                                
           KING                                                                 
           MILLER                                                               
        20 JONES                                                                
           FORD                                                                 
           ADAMS                                                                
           SMITH                                                            
           SCOTT                                                                
        30 WARD                                                                 
           TURNER                                                               
           ALLEN                                                                
           JAMES                                                           
           BLAKE                                                                
           MARTIN                                                               

14 rows selected.

SQL> clear breaks
breaks cleared
SQL> -- Without any help from SQL*Plus I tried two approaches.
SQL> -- Both with Oracle SQL analytic functions.

SQL> select decode (
  2    row_number() over (partition by deptno order by ename)
  3    ,1,deptno
  4    ) AS DEPTNO
  5    ,ename
  6  from emp
  7  order by emp.deptno
  8  ;

    DEPTNO ENAME                                                                
---------- ----------                                                           
        10 CLARK                                                                
           KING                                                                 
           MILLER                                                               
        20 ADAMS                                                                
           FORD                                                                 
           JONES                                                                
           SCOTT                                                                
           SMITH                                                            
        30 ALLEN                                                                
           BLAKE                                                                
           JAMES                                                        
           MARTIN                                                               
           TURNER                                                               
           WARD                                                                 

14 rows selected.

SQL> -- It is mandatory to use name of the table in the ORDER BY clause.
SQL> -- Next approach - to use LAG function to compare current
SQL> -- value of DEPTNO column with previous. If it is the same
SQL> -- then there is no output (null).
SQL>
SQL> select decode (
  2    lag(deptno) over (order by deptno)
  3    ,deptno,null,deptno
  4    ) as deptno
  5    ,ename
  6  from emp
  7  order by emp.deptno
  8  ;

    DEPTNO ENAME                                                                
---------- ----------                                                           
        10 CLARK                                                                
           KING                                                                 
           MILLER                                                               
        20 ADAMS                                                                
           FORD                                                                 
           JONES                                                                
           SCOTT                                                                
           SMITH                                                            
        30 ALLEN                                                                
           BLAKE                                                                
           JAMES                                                        
           MARTIN                                                               
           TURNER                                                               
           WARD                                                                 

14 rows selected.

SQL> -- Use TO_NUMBER function or another way to adjust the size
SQL> -- of DEPTNO column

Tuesday, October 9, 2012

Palindrome detector

The string is palindrom if reverted it equals to source string. Here is simple algorithm to detect is the source string a palindrome or not.

CREATE OR REPLACE PROCEDURE isPalindrom(l_string VARCHAR2)
IS
  src_string VARCHAR2(32767) := l_string;
  len_src_string INTEGER := LENGTH(src_string);
  j INTEGER;
  
  FUNCTION getChar(i INTEGER) RETURN CHAR
  IS
  BEGIN
    RETURN SUBSTR(src_string, i, 1);
  END;

BEGIN
  
  j := 1;
  LOOP
    EXIT WHEN (j = ROUND(len_src_string/2) + 1) 
              OR getChar(j) <> getChar(len_src_string-j+1);
    j := j+1;
  END LOOP;
  
  IF getChar(j) = getChar(len_src_string-j+1) THEN
    DBMS_OUTPUT.PUT_LINE(l_string||' IS POLYNDROM');
  ELSE
    DBMS_OUTPUT.PUT_LINE(l_string||' IS NOT POLYNDROM');
  END IF;


  EXCEPTION
  WHEN OTHERS THEN
     DBMS_OUTPUT.PUT_LINE('SQLCODE: ' || SQLCODE);
     DBMS_OUTPUT.PUT_LINE('SQLERRM: ' || SQLERRM);     
END;

Friday, August 24, 2012

Multiset operations with nested tables

Daily quizzes provided by www.plsqlchallenge.com are the right way to learn PL/SQL language, to know about many features, tips and trics. The site also allows to communicate with developers and learn the best practices.

PL/SQL Challenge suggested interesting task yesterday - to develop code "to compare row counts of all tables in two schema and display the names and counts of the tables where there is a disagreement." PL/SQL and SQL are both possible. I prefered PL/SQL way and tried to play around with nested tables and multiset operations. The procedure is shown below.
CREATE OR REPLACE PROCEDURE compare_table_counts(
   schema1_in IN VARCHAR2,
   schema2_in IN VARCHAR2,
   filter_in IN VARCHAR2)
IS

   TYPE type_aat IS TABLE OF VARCHAR2(100);
   tab_list_1 type_aat;
   tab_list_2 type_aat;
   tab_except type_aat;

   PROCEDURE make_tab_list(
      loc_tab IN OUT type_aat,schema_name IN VARCHAR2
   )
   IS
      sql_stmt VARCHAR2(200);
      loc_cnt PLS_INTEGER;
   BEGIN
      SELECT table_name
      BULK COLLECT INTO loc_tab
      FROM all_tables
      WHERE owner = schema_name
            AND table_name LIKE filter_in;

      FOR indx IN loc_tab.FIRST..loc_tab.LAST
      LOOP
         sql_stmt := 'SELECT COUNT(*) FROM '||
                      schema_name||'.'||
                      loc_tab(indx);
         EXECUTE IMMEDIATE sql_stmt INTO loc_cnt;
         loc_tab(indx) := loc_tab(indx) || ' (' ||
                                    loc_cnt || ')';
      END LOOP;
   END;

   PROCEDURE show_disagreement(
      schema_name IN VARCHAR2, loc_tab IN type_aat
   )
   IS
   BEGIN
      DBMS_OUTPUT.PUT_LINE('In schema '||schema_name||
                           ' differing tables...');
      DBMS_OUTPUT.PUT_LINE('(Row counts in parentheses)');
      FOR indx IN loc_tab.FIRST..loc_tab.LAST
      LOOP
         DBMS_OUTPUT.PUT_LINE(' ' || loc_tab(indx));
      END LOOP;
   END;

BEGIN
   make_tab_list(tab_list_1,schema1_in);
   make_tab_list(tab_list_2,schema2_in);

   tab_except := tab_list_1 MULTISET EXCEPT tab_list_2;
   IF tab_except.COUNT > 0 THEN
      show_disagreement(schema1_in,tab_except);
   END IF;

   tab_except := tab_list_2 MULTISET EXCEPT tab_list_1;
   IF tab_except.COUNT > 0 THEN
      show_disagreement(schema2_in,tab_except);
   END IF;

   EXCEPTION
      WHEN OTHERS THEN
         DBMS_OUTPUT.PUT_LINE('SQLCODE: ' || SQLCODE);
         DBMS_OUTPUT.PUT_LINE('SQLERRM: ' || SQLERRM);     
END;

Monday, August 20, 2012

"The keys to Oracle" by Tom Kyte

The very useful guide "how-to" bridle Oracle was written by Tom Kyte - http://tkyte.blogspot.com/2012/08/the-keys-to-oracle.html
There are also few very interesting comments including Steven Feuerstein's.

Sunday, August 19, 2012

Multiset Operations with Associative Arrays


It would be convenient to use multiset operations not only with nested tables but with associative arrays too. According to Oracle® Database SQL Language Reference multiset operations apply to nested tables only. For now. Some help would be possible to get from the package show below. The function AA_STRNDX build associative array as intersection or not intersection of two arrays. The OPER_ID parameter can take value 'INTERSECT' or 'NOT INTERSECT'.
create or replace
PACKAGE compare
IS

  SUBTYPE strndx_type IS VARCHAR2(1000);
  TYPE aa_strndx_type IS TABLE OF PLS_INTEGER
                          INDEX BY strndx_type;

  FUNCTION aa_strndx(
    array_in_1  IN  aa_strndx_type,
    array_in_2  IN  aa_strndx_type,
    array_out   OUT aa_strndx_type,
    oper_id     IN  VARCHAR2 := 'INTERSECT'
  ) RETURN PLS_INTEGER;

END;

Saturday, July 21, 2012

Set of links for OCP Advanced PL/SQL developer

I've finished creating all-in-one training kit for 1Z0-146 Oracle Exam. It is just a list of topics which completely corresponds to the exam. 1Z0-146 is the final step to become Oracle Advanced PL/SQL Developer Certified Professional.

Straight link to the Oracle documentation is created for each topic. For some subtopics there are recommended links. In some cases the one link can match to a few topics.

This all-in-one training kit was created using only free information sources. Self-made study guide was used by me to prepare for 1Z0-146. I have passed the exam successfully. If some links are not already active please contact me - kgkuzmin@gmail.com.

Good luck to you in your endeavor to become Oracle Certified Professional!