Jak v ASP.NET pracovat s SQL dotazem, který může vrátit NULL

30. 12. 2013, Vladimír Klaus, přečteno 2712x

ASP.NET/C#
SQL

Předpokládejme, že si chcete napsat funkci, která pomocí SQL dotazu vrátí součet cen.

Jak v ASP.NET pracovat s dotazem, který může vrátit NULL

Vycházíme přitom z dat této tabulky.

Jak v ASP.NET pracovat s dotazem, který může vrátit NULL

Není to nic těžkého, na to máme funkci QueryValue, takže funkce by mohla vypadat takto.

//první jednoduchá verze a)
public static decimal MujSoucet1() {
    var db = Database.Open("Data"); 
    var selectQueryString = "SELECT SUM(Cena) FROM MojeTabulka WHERE ID <= 2";
    var res = db.QueryValue(selectQueryString);
    return res;
}

Funkce vrátí 500. Skvělé, funguje to! Nyní změníme podmínku na ID > 2;

//první jednoduchá verze b)
public static decimal MujSoucet1() {
    var db = Database.Open("Data"); 
    var selectQueryString = "SELECT SUM(Cena) FROM MojeTabulka WHERE ID > 2";
    var res = db.QueryValue(selectQueryString);
    return res;
}

Výsledkem je ale chyba. Ceny v záznamech jsou NULL, že by to bylo tím?

Jak v ASP.NET pracovat s dotazem, který může vrátit NULL

Dobrá tedy, upravíme dotaz tak, aby si s takovou situací poradil a budeme NULL hlídat.  Vždyť přece součet NULL je také NULL.

    //druhá, o něco chytřejší verze
    public static decimal MujSoucet2() {
        var db = Database.Open("Data"); 
        var selectQueryString = "SELECT SUM(Cena) FROM MojeTabulka WHERE ID > 2";
        var res = db.QueryValue(selectQueryString);
        if(res != null) {
            return res;
        } else {
            return 0;
        }
    }

Jenže ono to zase neprojde. Proč? Asi by to chtělo si lépe přečíst onu chybu. Vrací totiž informaci o DBNull, nikoliv NULL.

Jak v ASP.NET pracovat s dotazem, který může vrátit NULL

Takže jak budeme testovat na DBNull? Úplně jinou funkcí. A také upravíme návratovou hodnotu třeba na -1 (může se to hodit), protože i součet cen může být 0 a nemohli bychom to odlišit od situace, kdy je to opravdu NULL.

    //třetí a nejchytřejší verze?
    public static decimal MujSoucet3() {
        var db = Database.Open("Data"); 
        var selectQueryString = "SELECT SUM(Cena) FROM MojeTabulka WHERE ID > 2";
        var res = db.QueryValue(selectQueryString);
        if(DBNull.Value.Equals(res)) {
            return -1;
        } else {
            return res;
        }
    }

Jak asi vidíte z příkladu, stejně není možné odlišit situaci, kdy záznamy existují, ale součet vrací NULL od situace, kdy žádné takové záznamy neexistují. Řešením mohou být dva dotazy.

    //čtvrtá úplně dokonalá verze
    public static decimal MujSoucet4(int IDVetsiNez) {
        var db = Database.Open("Data"); 
        var selectQueryString = "SELECT COUNT(*) FROM MojeTabulka WHERE ID > @0";
        var res = db.QueryValue(selectQueryString, IDVetsiNez);
        if(res != 0) {
            selectQueryString = "SELECT SUM(Cena) FROM MojeTabulka WHERE ID > @0";
            var resval = db.QueryValue(selectQueryString, IDVetsiNez);
            if(DBNull.Value.Equals(resval)) {
                //záznamy existují, ale VŠECHNY jejich ceny jsou NULL
                return -1;
            } else {
                //záznamy existují a bylo možné ceny (některé mohou být NULL) sečíst 
                return resval;
            }
        } else {
            //žádné záznamy neexistují, na sčítání vůbec nedojde!
            return -999;
        }
    }

Při volání s různými parametry...

<p>Součet cen: @MujSoucet4(0)</p>
<p>Součet cen: @MujSoucet4(2)</p>
<p>Součet cen: @MujSoucet4(30)</p>

...získáme již zcela korektní výsledek.

Součet cen: 500
Součet cen: -1
Součet cen: -999