30.12.2013, Vladimír Klaus, navštíveno 4999x

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