brainbaking/content/wiki/code/db/sql.md

6.8 KiB

+++ title = "sql" draft = false tags = [ "", "Users", "jefklak", "Downloads", "pages", "code", "db", "sql" ] date = "2013-09-03" +++

code:db >> Sql

Stored procedures

Oproepen

Zie http://stackoverflow.com/questions/3991721/run-stored-procedure-in-sql-developer

Gegeven: een procedure GETPERMISSIONSALL als volgt gedeclareerd:

create or replace 
PROCEDURE "GETPERMISSIONALL" ( v_actCreator in VARCHAR2, v_repGuid in VARCHAR2, v_userid INT, v_retHasPermission out SMALLINT)

De laatste parameter is de return waarde (kunnen meerdere dingen zijn dus). Hoe roep ik nu zoiets aan?

In SQL Developer

Definiëer eerst uw variabelen, om ze daarna eventueel af te drukken -

SET serveroutput on;
DECLARE
    v_actCreator VARCHAR2(100);
    v_repGuid VARCHAR2(100);
    v_userid int;
    Output smallint;
BEGIN
    /* Assign values to IN parameters */
    v_actCreator := '19980121164845.106.1 7';
    v_repGuid := '2466B39EFDA94D5E9249D252FF25C4D6';
    v_userid := 540;

    /* Call procedure within package, identifying schema if necessary */
    GETPERMISSIONALL(v_actCreator, v_repGuid, v_userid, Output);

    /* Display OUT parameters */
    dbms_output.put_line('Output: ' || Output);
END;
/
-- Display OUT parameters
-- print :Output;
Werken met REF CURSOR

Probleemstelling:

  1. Uw procedure retourneert een REF CURSOR als IN en OUT variabele
  2. Je wil over deze gaan loopen en ergens een resultaat afdrukken of vergelijken
  3. openen van een REF CURSOR gaat niet.

Gebruik in een LOOP dadelijk FETCH INTO:

SET serveroutput ON;
DECLARE
  P_USERID NUMBER;
  MY_P_CURSOR C2MV5_DEV.PKG_WFSEC.Validation_Send_Item_Cursor;
  cursor_element C2MV5_DEV.PKG_WFSEC.WFSEC_Send_Item_T;
BEGIN
  P_USERID := 11;
  
  PKG_WFSEC.FETCHSENDITEMS(
    P_USERID => P_USERID,
    P_CURSOR => MY_P_CURSOR
  );

 WHILE TRUE LOOP
    FETCH MY_P_CURSOR INTO cursor_element;
    EXIT WHEN MY_P_CURSOR%NOTFOUND;

    DBMS_OUTPUT.PUT_LINE(cursor_element.executingdoctorname);
  END LOOP;

END;

Merk op dat hier bijvoorbeeld cursor_element van een bepaald type is dat in een package gedefinieerd is, dat de cursor ook retourneert (TYPE Validation_Send_Item_Cursor IS REF CURSOR RETURN WFSEC_Send_Item_T;)

In C#

        public object QueryProcedure(string procedure, IDictionary<string, object> parameters, string outputParameter)
        {
            var command = new OleDbCommand(procedure);
            foreach (var item in parameters)
            {
                   command.Parameters.AddWithValue(item.Key, item.Value);
            }
            var output = command.Parameters.AddWithValue(outputParameter, 0);
            output.Direction = ParameterDirection.Output;

            QueryProcedure(command);
            return output.Value;
        }

Unit testen

Met SQL Developer

zie Oracle docs: Unit Testing in SQL Developer 3+

Read this first: Unit Testing PL/SQL In SQL Developer problems - toch niet zo geweldig?

Kan blijkbaar al niet apart gerund worden (mee in de build? een of nadere bat file van SQLDev zelf?)

Als uw stored procedure data wijzigt kan je in de startup en teardown process stappen toevoegen: "table or row copy", om die daarna terug te zetten.

Test gerelateerde tabellen opkuisen

Als je database-onafhankelijk testen wil draaien, kan je deze ook exporteren en importeren, en daarna runnen. Een import verwijdert echter niet oude niet-relevante unit tests, dus opkuisen van de UT repository is wel vereist. Hier is een SQL stored proc. om de boel te automatiseren:

SET serveroutput ON;
DECLARE
    queryStr VARCHAR2(100);
    tableName VARCHAR2(900);
    cursor tableCursor is SELECT table_name FROM dba_tables where owner ='user' and table_name like 'UT_TEST_%' or table_name like 'UT_SUITE_%';
BEGIN

dbms_output.enable(10000);
DBMS_OUTPUT.PUT_LINE('Cleaning unit test tables');

open tableCursor;
fetch tableCursor into tableName;

WHILE (tableCursor%FOUND) LOOP
  queryStr := 'delete from ' ||tableName;
  execute immediate queryStr;
  --DBMS_OUTPUT.PUT_LINE(queryStr);
  fetch tableCursor into tableName;
END LOOP;
close tableCursor;

commit;
DBMS_OUTPUT.PUT_LINE('SUCCESS - all cleaned!');

END;
/

Dit kan je runnen met sqlplus op de volgende manier:

sqlplus user/pass@db @"fullpath_to_filename.sql"

Let op met spaties in filename, gebruik daarom dubbele quotes hierboven.

Command-line testen runnen
ututil -run -suite -name [name] -repo [repo] -db [db] -log 3

Uw repository naam is dezelfde als de DB naam als je Tools > Unit Test > Create/Update Repository... gekozen hebt via SQL Developer.

ututil kan de db connectie namen niet resolven bij TNS - Gebruik Connection Identifier (copypaste desnoods van tnsnames.ora)

Fout die je anders krijgt:

D:<br/>oracle<br/>sqldeveloper<br/>sqldeveloper<br/>bin>ututil -run -test -name GETPERMISSIONALL -repo CHCDEV -db CHCTEST -log 3
SEVERE oracle.jdeveloper.db.ConnectionException: Could not connect to database CHCTEST. The error encountered was: Ongeldige verbindingsgegevens opgeg
even.
Controleer de notatie van de URL voor de opgegeven driver.
        oracle.jdeveloper.db.DatabaseConnections.getUniqueConnection(DatabaseConnections.java:514)
SEVERE null - oracle.dbtools.unit_test.utils.UtUtils.getRunnerConnection(UtUtils.java:141)
Unable to run test
Integratie met de build: C#

Een SqlDeveloperTest klasse die het volgende doet:

  1. voer sqlplus uit met bovenstaande cleanup script dat alle repository tabellen cleart
  2. voer ututil uit met -imp -repo [repo] -file [xmlfile]
  3. voer ututil uit met -run zoals boven aangegeven.

De eigenlijke unit testen leven dan in uw source control omgeving zoals TFS, in die ene xml file. Op die manier kan je branchen en zo zonder dat die testen vasthangen aan je DB schema. Joepie!

Problemen

NOT IN retourneert geen enkele resultaten ??

voorbeeld:

select * from sch_vac.VAC_TAKEN 
where  ikl_id is not null
and ikl_id not in (select iklnummer from VAC_WERFRES_KANDIDATEN)

There are serious problems with subqueries that may return NULL values. It is a good idea to discourage the use of the NOT IN clause (which invokes a subquery) and to prefer NOT EXISTS (which invokes a correlated subquery), since the query returns no rows if any rows returned by the subquery contain null values.

Oplossing is dus:

select * from vac_taken taak where ikl_id is not null
and not exists (select 1 from vac_werfres_kandidaten where iklnummer = taak.ikl_id)