2016. június 28., kedd

NULLIF kontra ISNULL valamint a nullával való osztás kezelése

A NULLIF és az ISNULL ránézésre hasonlónak tűnhetnek, viszont meglehetősen eltérő módon viselkednek.


NULLIF

Szintaxis: NULLIF(expression, expression)

Ha a két kifejezés értéke különbözik, akkor visszaadja az első kifejezést. Ha viszont megegyeznek, akkor NULL-t ad vissza. Egyszerűbb SQL utasítás érhető el vele, mintha a CASE-t használnánk.

NULLIF és CASE összehasonlítására példa az Books Online-ból (BOL):
USE AdventureWorks2012; 
GO 
SELECT ProductID, MakeFlag, FinishedGoodsFlag,  
   NULLIF(MakeFlag,FinishedGoodsFlag)AS 'Null if Equal' 
FROM Production.Product 
WHERE ProductID < 10; 
GO 
 
SELECT ProductID, MakeFlag, FinishedGoodsFlag,'Null if Equal' = 
   CASE 
       WHEN MakeFlag = FinishedGoodsFlag THEN NULL 
       ELSE MakeFlag 
   END 
FROM Production.Product 
WHERE ProductID < 10; 
GO 


ISNULL

Szintaxis: ISNULL(check_expression, replacement_value)

Ha a vizsgálandó első paraméter nem NULL, akkor azt adja vissza, ellenkező esetben a helyettesítő értéket. Kiválóan alkalmas olyan esetekben például, amikor valamilyen alapértelmezett értéket szeretnénk használni, ha egyébként NULL lenne. A COALESCE utasítás egy speciális esetének is felfogható, amikor csak két paramétert kapott és abból kell visszaadnia az első nem NULL értéket.

Jó példa az aggregáló műveletekre szintén az MSDN-ről:
USE AdventureWorks2012; 
GO 
SELECT AVG(ISNULL(Weight, 50)) 
FROM Production.Product; 
GO 

Az aggregáló függvényeknél erősen ajánlott végiggondolni, hogy kellene-e használni, mert ezeknél a függvényeknél, ha legalább egy elem NULL, akkor az eredmény is NULL lesz.


Közös példa: nullával való osztás 
A kettő kombinálására egy jó példa a nullával való osztás kezelése. Először a NULLIF segítségével kezeljük, hogy ha nullával osztanánk, akkor ne dobjon hibát, ekkor ugyanis NULL lesz az eredmény.
SELECT @osztando / NULLIF( @oszto, 0 ) AS value

Majd erre hívjuk meg az ISNULL-t, hogy ilyenkor nullát adjon vissza és kész is:
SELECT ISNULL( @osztando / NULLIF( @oszto, 0 ), 0) AS value

Entity Frameworkből UDT paraméterű tárolt eljárás futtatása

Az Entity Framework alaphangon nem támogatja a saját SQL típust, vagyis a User Defined Type-ot. Ha egy olyan tárolt eljárást szeretnénk importálni az EF-fel, ami UDT típusú paramétert vár, akkor ugyan nem fog hibát dobni, de nem is fogja legenerálni a hozzátartozó kódot.

Ennek áthidalására egy jó módszer az EntityFrameworkExtras nevű NuGettel is elérhető csomag, aminek segítségével típusosan lehet ilyen tárolt eljárást futtatni. A bekötéséhez az alábbi néhány lépés szükséges.

1. UDT létrehozása MS SQL Server adatbázisban

CREATE TYPE [dbo].[TEMP_IDTABLE] AS TABLE(
       [ID] [int] NULL
)
GO

2. Ezt a típust paraméterként használó tárolt eljárás létrehozása

CREATE PROCEDURE [dbo].[DummyStoredProcedure]
       @myValues [Temp_IDTABLE] READONLY
AS
BEGIN
       -- értelmes logika
       SELECT * FROM @myValues
END

3. A használt EF verziónak megfelelő EntityFrameworkExtras hozzáadása a projekthez
  • EF 5: EntityFrameworkExtras.EF5
  • EF 6: EntityFrameworkExtras.EF6

4. Létre kell hozni egy osztályt, ami majd az 1. lépésben elkészült UDT-t fogja reprezentálni

[UserDefinedTableType("TEMP_IDTABLE")]
public class TempIdTable
{
    [UserDefinedTableTypeColumn(1, Name = "ID")]
    public int? ID { get; set; }
}

Természetesen az osztály és a mezők neve bármi lehet, mivel az attribútumokkal lesz beállítva, hogy az adatbázisban mire kell majd leképezni.

5. Az előbbihez hasonlóan a tárolt eljáráshoz kell egy osztály

[StoredProcedure("DummyStoredProcedure")]
public class DummyStoredProcedure
{
    [StoredProcedureParameter(SqlDbType.Udt, ParameterName = "myValues")]
    public List<TempIdTable> MyValues { get; set; }
}

6. Ezek után már csak meg kell hívni az eljárást. Ehhez az EntityFrameworkExtras tartalmaz Extended Methodokat, amik a DbObjectre illetve az ObjectContextre akadnak rá, és olyan objektumokat várnak, amik el vannak látva a StoredProcedure attribútummal.

ObjectContext oc = new ObjectContext("ConnectionString");

var sp = new DummyStoredProcedure
{
    MyValues = new List<TempIdTable>
    {
        new TempIdTable { ID = 1 },
        new TempIdTable { ID = 2 }
    }
};

IEnumerable<int> results = oc.ExecuteStoredProcedure<int>(sp);

2016. június 5., vasárnap

SSMS IntelliSense Cache frissítése

Időnként előfordul, hogy miután létrehoztam, módosítottam esetleg töröltem valamilyen objektumot, az SSMS hibát jelez olyan SQL utasításokban, amik az érintett objektumra hivatkoznak. Annak ellenére, hogy az SQL script sikeresen lefutna, eléggé zavaró, amikor bemutatásnál vagy megbeszélésen piros hibajelzések tarkítják a kódot. Ezt az okozza, hogy az SSMS-ben lévő IntelliSense Cache még nem frissült a változtatás óta.

Szerencsére többféleképpen is ki lehet kényszeríteni, hogy frissüljön:
  • Gyorsgombok segítségével: CRTL+SHIFT+R
  • Menüben kikeresve: Edit / IntelliSense / Refresh Local Cache

HTML FORM-ban egy darab inputbox

A HTML FORM tagnek van egy olyan tulajdonsága, hogy ha csak egy darab, egy soros beviteli mezőt tartalmaz (<input type="text"/>), akkor az ENTER lenyomására mindig lefut a SUBMIT, függetlenül attól, hogy van-e rá beállítva alapértelmezett gomb.

Célszerű az alábbiakat ellenőrizni, ha az ENTERre olyankor is újratölti az oldalt, amikor nem kellene:
  • csak egy darab beviteli mezőt tartalmaz a FORM
  • van submit típusú gomb (<input type="submit"/>)
  • van BUTTON tag és annak milyen értékű a type attribútuma, mert a submit az alapértelmezett
  • van valami JavaScript függvény beregisztrálva a FORM-ra, a beviteli mezőre vagy esetleg az egyik gombra a FORM-hoz kapcsolódóan