Hierarchisch verknotete Datensätze mit SP nach XML umformen

Na ja, damit meine ich jetzt nicht unbedingt die Geschwindigkeit, sondern die Sicherheit und gewisse Sachen welche in SQL schlicht einfacher sind.

"In Oracle wird es nurnoch auf der Java-Ebene langsamer..."

Wie meinst du das jetzt? UDF's über Java programmieren? (Wie man es bei SQL Server mit .NET machen kann?)
Oder bspw. die Daten von einem SELECT resultset in einer J2EE-Webapp abarbeiten um damit bspw. Datensätze mit mehreren Unterelementen auf dem GUI zu rendern?

P.S.: Ich komme übrigens aus dem Java/J2EE-Bereich, ist allerdings schon ein paar Jahre her...

Grüsse, Jan
 
Werbung:
Das dürfte sich grundsätzlich bei jeder Datenbank ähnlich verhalten, also sage ich erstmal ja, das ist mit SQL Server und .NET vergleichbar...
In einer Oracle Datenbank kann man Java-Klassen einbinden...
Hierarschich sieht das ganze etwa so aus:
1. Java
2. PL/SQL
3. SQL

Performanceweise sollte man immer versuchen so "tief" wie möglich zu bleiben... Allerdings gibt es, je "tiefer" man geht, immer mehr einschränkungen...
Gerade was Typen-Definition angeht macht es sich schnell bemerkbar auf welchem Layer man sich befindet (Bsp.: In PL/SQL definierte Objekte können nicht in SQL verwendet werden... Andersherum aber schon)
 
Also eine höhere Programmiersprache bei Functions zu verwenden macht meistens nur dann "Sinn", wenn man Sachen auf der DB macht welche eigentlich gar nix mit der DB zu tun haben. Mailversand per Trigger, Zugriff auf ein SOAP-Service, Zugriff auf einen anderen DB-Server, OS-Event-Log schreiben und solche Sachen...

ODER: Wenn eine SQL Server table-valued-UDF programmiert wird ich es gemacht habe, welche die Börsenkurse von Google Finance abfragen und als Resultset ausgeben kann. ;)

Der SQL Server fungiert dann als alles mögliche, nur nicht als Datenspeicher...

SELECT * FROM getStockQuoteData(symbol, marketplace, fromDate, endDate)
; oder so ähnlich, habe es gerade nicht im Kopf...


(HTTP-GET liefert CSV, das wird in der .NET UDF in ein Resultset umgewandelt...)

Die Daten kann ich dann bspw. ins Excel importieren, und dort was damit machen...


Grüsse, Jan
 
Also eine höhere Programmiersprache bei Functions zu verwenden macht meistens nur dann "Sinn", wenn man Sachen auf der DB macht welche eigentlich gar nix mit der DB zu tun haben. Mailversand per Trigger, Zugriff auf ein SOAP-Service, Zugriff auf einen anderen DB-Server, OS-Event-Log schreiben und solche Sachen...
Oder wenn man BL zentral in der DB haben will ;)
Dann muss der Client keine "Berechnungen" mehr vornehmen, sondern sendet nurnoch Parameter an eine SP...
Egal welche DB... Das ist eig. überall best practice...
 
Wie gesagt kannte ich bisher eigentlich nur ORM/CRUD, da ich aus der OO/Java-Welt komme... dort sind solche Sachen meiner Meinung nach alles andere als best practise...

Anders sieht es evtl. bei VB(Script)/ASP/C/C++/COBOL/Oracle Forms/FoxPro aus.

Mein letzter Job hatte mit einer Software zu tun wechle vor ca. 15 Jahren auf "Classic ASP" entwickelt wurde... die neuste Version war eine Mischung aus Classic ASP und .NET... dort wurde immer noch ALLES mit dem alten SP's gemacht, keine einzige Sache verwendet dort ORM oder so...

Grüsse, Jan
 
"Oder wenn man BL zentral in der DB haben will ;)"

Aber für was muss ich dann meine SQL Server UDF's mit .NET "erweitern" und die von Oracle mit Java?

Für "normale" BL reicht es doch meist locker aus, Functions mit 100% SQL zu verwenden...?
 
Bei SQL Server sieht das so aus, wenn hinter der UDF eine Funktion ("static"!) ist welche sich bspw. in einer mit c# geschreibenen Klasse befindet, und die Logik der UDF nicht in SQL selbst geschreiben ist (ausser die Sachen für die Rückgabe):
Code:
USE test
  GO

IF EXISTS (SELECT ('') FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Quotes]') AND TYPE IN (N'FN', N'IF', N'TF', N'FS', N'FT'))
BEGIN
  DROP FUNCTION [dbo].[Quotes]
END
  GO

CREATE FUNCTION [dbo].[Quotes]
(
  @exchange NVARCHAR(8),
  @symbol NVARCHAR(8),
  @startDate NVARCHAR(10),
  @endDate NVARCHAR(10)
)
RETURNS TABLE
(
  [cnt]           INT,               
  [date]          DATETIME,          
  [open]          DECIMAL (28, 9),   
  [high]          DECIMAL (28, 9),   
  [low]           DECIMAL (28, 9),  
  [close]         DECIMAL (28, 9),   
  [vol]           INT
)
WITH EXECUTE AS CALLER

AS EXTERNAL NAME DotNetUDFTest.[DotNetUDFTest.DotNetUDFTestLib].Quotes
  GO




Ich meine damit NICHT eine UDF über eine höhere Programmiersprache anzusprechen, sondern umgekehrt!! ;-)


J# Code hier:

Code:
package DotNetUDFTest;

import System.*;
import System.Data.*;
import System.Data.SqlTypes.*;
import Microsoft.SqlServer.Server.*;

/*
import yahooquote.app.YahooQuote;
import yahooquote.beans.YahooQuoteBean;
*/

import gssc.*;

import System.Collections.*;

public class DotNetUDFTestLib
{
    private static final DotNetUDFTestLib instance = new DotNetUDFTestLib();

    private static volatile Hashtable instancePool = new Hashtable();

    private static int counter = 0;

    /** @attribute Microsoft.SqlServer.Server.SqlProcedure() */
    public static void getTestString()
    {
        SqlContext.get_Pipe().Send("Hello world!!!");
    }

    /** @attribute Microsoft.SqlServer.Server.SqlProcedure() */
    public static void getTestResultset()
    {
        SqlPipe lSqlPipe = SqlContext.get_Pipe();
        SqlMetaData[] cols = new SqlMetaData[1];
        cols[0] = new SqlMetaData("TEST_STRING", SqlDbType.NVarChar, 1024);

        SqlDataRecord lSqlDataRecord = new SqlDataRecord(cols);
        lSqlPipe.SendResultsStart(lSqlDataRecord);

        for (int cnt = 0; cnt < 100; cnt++)
        {
            lSqlDataRecord.SetSqlString(0, new SqlString("test" + cnt));
            lSqlPipe.SendResultsRow(lSqlDataRecord);
        }

        lSqlPipe.SendResultsEnd();
    }

    /** @attribute Microsoft.SqlServer.Server.SqlFunction(FillRowMethodName = "FillRowCustomTable") */
    public static IEnumerable Quotes(String exchange, String symbol, String startDate, String endDate)
    {
        counter++;
        instancePool.Add(new Integer(counter), new Integer(0));
       
        /*
        YahooQuote yq = new YahooQuote();
        yq.init("ichart.yahoo.com", "80", null);
        return yq.getStockQuoteDataArr(exchange, symbol, startDate, endDate);
        */

        GoogleStockQuoteServiceClient gssc = GoogleStockQuoteServiceClient.createInstance();
        return gssc.getQuotes(exchange, symbol, startDate, endDate);
    }

    public static void FillRowCustomTable(
        Object resultObj,
        /** @ref */ SqlInt32 cnt,
        /** @ref */ SqlDateTime date,
        /** @ref */ SqlDecimal open,
        /** @ref */ SqlDecimal high,
        /** @ref */ SqlDecimal low,
        /** @ref */ SqlDecimal close,
        /** @ref */ SqlInt32 vol)
    {
        int i = ((Integer)instancePool.get_Item(new Integer(DotNetUDFTest.DotNetUDFTestLib.counter))).intValue();
        instancePool.set_Item(new Integer(DotNetUDFTest.DotNetUDFTestLib.counter), new Integer(++i));

        /*
        YahooQuoteBean yqb = (YahooQuoteBean)resultObj;
        cnt = new SqlInt32(i - 1);
        date = new SqlDateTime(DateTime.ParseExact(yqb.getDate(), "dd.MM.yyyy", null));
        open = new SqlDecimal(yqb.getOpen());
        high = new SqlDecimal(yqb.getHigh());
        low = new SqlDecimal(yqb.getLow());
        close = new SqlDecimal(yqb.getClose());
        vol = new SqlInt32(yqb.getVol());
        est = new SqlDecimal(yqb.getEstim());
        */

        GoogleStockQuoteBeanI gsq = (GoogleStockQuoteBeanI)resultObj;
        cnt = new SqlInt32(i - 1);
        date = new SqlDateTime(DateTime.ParseExact(gsq.getDate(), "yyyy-MM-dd", null));
        open = new SqlDecimal(gsq.getOpen());
        high = new SqlDecimal(gsq.getHigh());
        low = new SqlDecimal(gsq.getLow());
        close = new SqlDecimal(gsq.getClose());
        vol = new SqlInt32(gsq.getVolume());
        // est = new SqlDecimal(yqb.getEstim());
    }



    /** @attribute Microsoft.SqlServer.Server.SqlProcedure() */
    public static void getQuotes(SqlString exchange, SqlString symbol, SqlString startDate, SqlString endDate)
    {
        SqlPipe lSqlPipe = SqlContext.get_Pipe();
        SqlMetaData[] cols = new SqlMetaData[8];

        int colsOrdinal = 0;
        cols[colsOrdinal++] = new SqlMetaData("cnt", SqlDbType.Int); // counter
        cols[colsOrdinal++] = new SqlMetaData("date", SqlDbType.DateTime); // date
        cols[colsOrdinal++] = new SqlMetaData("open", SqlDbType.Decimal, (ubyte)28, (ubyte)9); // open price
        cols[colsOrdinal++] = new SqlMetaData("high", SqlDbType.Decimal, (ubyte)28, (ubyte)9); // high price
        cols[colsOrdinal++] = new SqlMetaData("low", SqlDbType.Decimal, (ubyte)28, (ubyte)9); // low price
        cols[colsOrdinal++] = new SqlMetaData("close", SqlDbType.Decimal, (ubyte)28, (ubyte)9); // close price
        cols[colsOrdinal++] = new SqlMetaData("vol", SqlDbType.Int); // volume
        cols[colsOrdinal++] = new SqlMetaData("est", SqlDbType.Decimal, (ubyte)28, (ubyte)9); // estimation

        SqlDataRecord lSqlDataRecord = new SqlDataRecord(cols);
        lSqlPipe.SendResultsStart(lSqlDataRecord);

        /*
        YahooQuote yq = new YahooQuote();
        yq.init("ichart.yahoo.com", "80", null);
        */

        GoogleStockQuoteServiceClient gsq = GoogleStockQuoteServiceClient.createInstance();

        // Object[] lst = yq.getStockQuoteDataArr(exchange.ToString(), symbol.ToString(), startDate.ToString(), endDate.ToString());

        Object[] lst = gsq.getQuotes(exchange.ToString(), symbol.ToString(), startDate.ToString(), endDate.ToString());


        for (int cnt = 0; cnt < lst.length; cnt++)
        {
            colsOrdinal = 0;

            /*
            YahooQuoteBean entry = (YahooQuoteBean)lst[cnt];

            ////////
            lSqlDataRecord.SetSqlInt32(colsOrdinal++, new SqlInt32(cnt));
            lSqlDataRecord.SetSqlDateTime(colsOrdinal++, new SqlDateTime(DateTime.ParseExact(entry.getDate(), "dd.MM.yyyy", null)));
            lSqlDataRecord.SetSqlDecimal(colsOrdinal++, new SqlDecimal(entry.getOpen()));
            lSqlDataRecord.SetSqlDecimal(colsOrdinal++, new SqlDecimal(entry.getHigh()));
            lSqlDataRecord.SetSqlDecimal(colsOrdinal++, new SqlDecimal(entry.getLow()));
            lSqlDataRecord.SetSqlDecimal(colsOrdinal++, new SqlDecimal(entry.getClose()));
            lSqlDataRecord.SetSqlInt32(colsOrdinal++, new SqlInt32(entry.getVol()));
            lSqlDataRecord.SetSqlDecimal(colsOrdinal++, new SqlDecimal(entry.getEstim()));
            ////////
            */


            GoogleStockQuoteBeanI entry = (GoogleStockQuoteBeanI)lst[cnt];

            ////////
            lSqlDataRecord.SetSqlInt32(colsOrdinal++, new SqlInt32(cnt));
            lSqlDataRecord.SetSqlDateTime(colsOrdinal++, new SqlDateTime(DateTime.ParseExact(entry.getDate(), "dd.MM.yyyy", null)));
            lSqlDataRecord.SetSqlDecimal(colsOrdinal++, new SqlDecimal(entry.getOpen()));
            lSqlDataRecord.SetSqlDecimal(colsOrdinal++, new SqlDecimal(entry.getHigh()));
            lSqlDataRecord.SetSqlDecimal(colsOrdinal++, new SqlDecimal(entry.getLow()));
            lSqlDataRecord.SetSqlDecimal(colsOrdinal++, new SqlDecimal(entry.getClose()));
            lSqlDataRecord.SetSqlInt32(colsOrdinal++, new SqlInt32(entry.getVolume()));
           
            ////////


            lSqlPipe.SendResultsRow(lSqlDataRecord);
        }

        lSqlPipe.SendResultsEnd();
    }
}



...die Sache mit der Hashtable muss ich noch anschauen, sollte als Cache fungieren...
 
Wir sind ein wenig vom Thema abgekommen...


Hier noch mein Code mit Tiefenzähler:

Code:
DELIMITER //

DROP PROCEDURE IF EXISTS `getNestedXMLTree`;

CREATE PROCEDURE `getNestedXMLTree` (IN aRootNodeID INT, OUT aOutput VARCHAR(21844) CHARACTER SET utf8, IN aDepth INT)
BEGIN
    -- LOCAL VARIABLES
    DECLARE lDone  BOOLEAN DEFAULT FALSE;
   
    -- LOCAL VARIABLES FOR TABLE FIELDS
    DECLARE _adr_id  INT;
    DECLARE _name  VARCHAR(64);
    DECLARE _nodeOf  INT;
   
    -- LOCAL CURSOR VARIABLES
    DECLARE lCur CURSOR FOR
    SELECT
        adr.adr_id,
        adr.name,
        adr.nodeOf
   
    FROM
        CUST_v_adressen adr
       
    WHERE
        adr.nodeOf = COALESCE(aRootNodeID,0)
       
    ORDER BY
            adr.name ASC;
       
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET lDone = TRUE;
 
 
    -- CHECK aOutput FOR NULL AND SET TO EMPTY IF TRUE
    -- WE NEED THIS FOR THE FIRST RECURSION
    IF ISNULL(aOutput) THEN
        SET aOutput = '';
    END IF;
   
    -- CHECK aDepth FOR NULL AND SET TO -1 IF TRUE
    -- WE NEED THIS FOR THE FIRST RECURSION
    IF ISNULL(aDepth) THEN
        SET aDepth = -1;
    END IF;
   
   
    SET lDone = FALSE;
    OPEN lCur;
        lLoop: WHILE(!lDone) DO
            FETCH lCur INTO _adr_id, _name, _nodeOf;
            IF lDone THEN
                LEAVE lLoop;
            END IF;
            SET aDepth = aDepth + 1;
            IF (SELECT COUNT('') FROM CUST_v_adressen adr WHERE adr.nodeOf = _adr_id) > 0 THEN
                SET aOutput = CONCAT(aOutput, CONCAT('<entry id="', CONCAT(_adr_id,CONCAT('" value="', CONCAT(_name,CONCAT('" depth="',CONCAT(aDepth,'">')))))));
                CALL getNestedXMLTree(_adr_id, @aOutput, aDepth);
                SET aOutput = CONCAT(CONCAT(aOutput, @aOutput), '</entry>');
            ELSE
                SET aOutput = CONCAT(aOutput, CONCAT('<entry id="', CONCAT(_adr_id,CONCAT('" value="', CONCAT(_name,CONCAT('" depth="',CONCAT(aDepth,'" />')))))));
            END IF;
            SET aDepth = aDepth - 1;
        END WHILE lLoop;
    CLOSE lCur;
   
   
END; //
DELIMITER ;
 
Werbung:
evtl noch:

Code:
ORDER BY
adr.name ASC,
adr.adr_id ASC;

Dann ist wenigestens absolut klar was passiert (wer zuerst aufgelistet wird) wenn 2 Namen exakt gleich sind...
 
Zurück
Oben