Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   C++ Builder (https://www.clubdelphi.com/foros/forumdisplay.php?f=13)
-   -   Comprobar si un valor es nulo (https://www.clubdelphi.com/foros/showthread.php?t=89876)

Angel.Matilla 23-02-2016 13:51:13

Comprobar si un valor es nulo
 
Me estoy volviedo loco. Estoy ejecutando este query en Builder 6 contra una base de datos Firebird:
Código SQL [-]
SELECT DISTINCT A.Situacion, B.Literal FROM Persona A LEFT JOIN Instalacion B ON A.Situacion = B.Valor AND B.Etiqueta = 'Situacion'
WHERE A.CodPrv = (SELECT Literal FROM Instalacion WHERE Etiqueta = 'Provincia') AND A.Sexo = 0 ORDER BY Situacion
que me devuelve este resultado:
Cita:

SITUACION LITERAL
-2 DESCONOCIDA
0 SITUACIÓN 1
1 SITUACIÓN 2
2 SITUACIÓN 3
3 SITUACIÓN 4
4 Null

Todo esto para cargar un TCheckListBox. Lo que pretendo es que me cargue un único ítem con el valor desconocida, de forma que no haya un ítem vacío. He probado de todo; ahora lo tengo puesto así:
Código:

for (; !fPersona->Query->Eof; fPersona->Query->Next())
{
    cAux = UpperCase(fPersona->Query->FieldByName("Literal")->AsString);
    if (lPrueba && (cAux == "DESCONOCIDA" || cAux == NULL))
    {
          CheckListBox2->Items->Add("Desconocida");
          lPrueba = true;
    }
    else if (cAux != NULL)
          CheckListBox2->Items->Add(fPersona->Query->FieldByName("Literal")->AsString);
}

pero también he probado con
Cita:

fPersona->Query->FieldByName("Literal")->IsNull
fPersona->Query->FieldByName("Literal")->AsString == ""
fPersona->Query->FieldByName("Literal")->AsString.IsEmpty()
Y siempre me añade al final un ítem con el caption vacío:

Casimiro Noteví 23-02-2016 14:11:50

Puedes usar coalesce.
Código SQL [-]
SELECT DISTINCT A.Situacion, coalesce(B.Literal,'esto es nulo') FROM Persona ...

AgustinOrtu 23-02-2016 14:16:27

Código PHP:

for (; !fPersona->Query->EoffPersona->Query->Next())
{
    if (
fPersona->Query->FieldByName("Literal")->IsNull)      
        Continue;
 
    
cAux UpperCase(fPersona->Query->FieldByName("Literal")->AsString);
    ...


AsString esta retornando un string vacio (''), no retorna NULL cuando el TField tiene valor Null

ecfisa 23-02-2016 14:21:21

Hola.
Cita:

Empezado por Angel.Matilla (Mensaje 502466)
...
que me devuelve este resultado:
Código:

SITUACION  LITERAL
-2        DESCONOCIDA
 0            SITUACIÓN 1
 1            SITUACIÓN 2
 2        SITUACIÓN 3
 3        SITUACIÓN 4
 4        Null


¿ Y que resultado deseas obtener ?

Si deseas que cuando sea NULL figure DESCONOCIDA, podes hacer lo que te indicó Casimiro(es mejor) o tambíen:
Código PHP:

{
  
TIBQuery *qy static_cast<TIBQuery*>(fPersona->Query);

  for(
qy->First(); !qy->Eofqy->Next()) {
    
TField *fld qy->FieldByName("LITERAL");
    if (
fld->AsString == "DESCONOCIDA" || fld->IsNull)
      
CheckListBox1->Items->Add("Desconocida");
    else
      
CheckListBox1->Items->Add(fld->AsString);
  }
 ... 

que verifica la condición NULL del campo de forma similar a la respuesta de Agustín

Saludos :)

Angel.Matilla 23-02-2016 18:03:36

Cita:

Empezado por Casimiro Notevi (Mensaje 502468)
Puedes usar coalesce.

He usado tan pocas veces este comando que nunca me acuerdo de que existe. Es una buena idea.
Cita:

Empezado por AgustinOrtu (Mensaje 502469)
if (fPersona->Query->FieldByName("Literal")->IsNull)

Si te fijas es una de las cosas que he probado pero tampoco me funciona.
Cita:

Empezado por ecfisa (Mensaje 502470)
¿ Y que resultado deseas obtener ?

No, si el problema no es el resultado obtenido. De hecho cuento con él porque lo he provocado yo.
Cita:

Empezado por ecfisa (Mensaje 502470)
TField *fld = qy->FieldByName("LITERAL");

Este si funciona.

Muchas gracias a todos

AgustinOrtu 23-02-2016 20:01:31

Como lo resolviste? IsNull funciona, ahora el caso es como se usa dentro de los condicionales claro

Angel.Matilla 24-02-2016 09:46:22

Cita:

Empezado por AgustinOrtu (Mensaje 502512)
Como lo resolviste? IsNull funciona, ahora el caso es como se usa dentro de los condicionales claro

Así:
Código:

bool lPrueba = false;
for (; !fPersona->Query->Eof; fPersona->Query->Next())
    if (UpperCase(fPersona->Query->FieldByName("Literal")->AsString) != "DESCONOCIDA")
          CheckListBox2->Items->Add(fPersona->Query->FieldByName("Literal")->AsString);
    else if (!lPrueba)
    {
          CheckListBox2->Items->Add("Desconocida");
          lPrueba = true;
    }

Tuve que darle la vuelta al condicional porque si lo dejaba como estaba (con == en vez de !=) me seguía sacando dos veces ese literal, aunque todavía se puede mejorar ese código.

Lo de IsNull si que me ha extrañado que no funcionara porque de hecho le he usado con relativa frecuencia, tanto en BB.DD. Firebird como Paradox.

mamcx 24-02-2016 16:37:39

No uso C++ (que afortunado!) pero ya que no te han explicado porque pasa esto, aqui va algo pa que no olvides, y que muchos lenguajes es lo mismo:
Cita:

Empezado por Angel.Matilla (Mensaje 502466)
cAux = UpperCase(fPersona->Query->FieldByName("Literal")->AsString);
if (lPrueba && (cAux == "DESCONOCIDA" || cAux == NULL))


https://stackoverflow.com/questions/...uit-evaluation

Por regla general, los AND y los OR utilizan "short circuit" y siguen la regla de las tablas de verdad. En este caso, si la 1era opcion en AND es verdadera, NO CHEQUEA LA SEGUNDA. Y lo mismo aplica en el OR

True And False = True
False OR True = True

El segundo problema es que UpperCase si te esta volviendo NULL? O cadena vacia?.

AgustinOrtu 24-02-2016 16:41:17

En un AND donde encuentra el primer falso ya no chequea más
En un OR, con un sólo verdadero ya alcanza para que sea verdadero

Basicamente

(True) AND (True) AND (True) AND (True) AND (False) = False
(False) AND (True) AND (True) AND (True) AND (True) = False, solo se evalua el primer condicional

(True) OR (True) OR (True) OR (True) OR (False) = True, solo se evalua el primer condicional

Tiene cierto "truco", ya que a pesar de que aca estamos hablando de constantes True/False, muchas veces tenemos funciones que devuelven True o False. Y si bien es un error grave que tengan efectos colaterales, es para tener en cuenta, no se debe asumir que siempre se evaluan todas las condiciones; tambien es para tener en cuenta el orden en que encadenamos condiciones, lo propio es poner lo mas rapido primero y dejar lo pesado para el final

Se puede configurar el compilador para que haga Full Boolean Evaluation

Neftali [Germán.Estévez] 25-02-2016 09:29:43

No se si alguien lo ha comentado ya, pero ¿no puedes añadir la condición a la SQL, en lugar de tratarlo a posteriori?

Código SQL [-]
 ... AND (LITERAL IS NOT NULL)

Angel.Matilla 25-02-2016 09:38:03

Cita:

Empezado por Neftali (Mensaje 502624)
... AND (LITERAL IS NOT NULL)

Esa condición no me vale. Fíjate que lo que me interesa es precisamente que salgan los valores nulos para saber que registros están mal grabados y la tabla en la que voy a buscar los literales no está indexada, ni me interesa, ya que es donde se guardan los valores básicos de configuración.

ecfisa 25-02-2016 12:59:38

Hola.

La situación no me termina de quedar clara... Cuando el valor de la columna LITERAL es NULL, ¿ Deseas que el ítem del CheckListBox incluya algún texto como sugiere la imágen de tu primer mensaje ?

Si no te interpreté mal y es así, creo que lo mas simple es hacerlo con la sugerencia de Casimiro, ejemplo :
Código PHP:

...
void __fastcall TForm1::FormCreate(TObject *Sender)
{
  
TIBQuery *qy static_cast<TIBQuery*>(IBQuery1);

  
qy->Close();
  
qy->SQL->Clear();
  
qy->SQL->Add"SELECT DISTINCT A.SITUACION," );
  
qy->SQL->Add"COALESCE(UPPER(B.LITERAL), 'NULO') AS LITERAL" );
  
qy->SQL->Add"FROM PERSONA A LEFT JOIN INSTALACION" );
  
qy->SQL->Add"B ON A.SITUACION = B.VALOR ..." );
  ...
  for ( 
qy->Open(); !qy->Eofqy->Next() )
    
CheckListBox1->Items->Add(qy->FieldByName("LITERAL")->AsString );
  
CheckListBox1->Style lbOwnerDrawFixed;
}

void __fastcall TForm1::CheckListBox1DrawItem(TWinControl *Control,
      
int IndexTRect &RectTOwnerDrawState State)
{
  
TCheckListBox *lb static_cast<TCheckListBox*>(Control);
  
AnsiString str lb->Items->Strings[Index];

  if ( 
str == "NULO" ) {
    
lb->Canvas->Font->Color clRed;
    
lb->Canvas->Font->Style TFontStyles() << fsBold << fsUnderline;
  }
  
lb->Canvas->FillRectRect );
  
lb->Canvas->TextOutRect.leftRect.topstr );


Muestra:


Saludos :)

Angel.Matilla 25-02-2016 13:48:58

Cita:

Empezado por ecfisa (Mensaje 502643)
La situación no me termina de quedar clara... Cuando el valor de la columna LITERAL es NULL, ¿ Deseas que el ítem del CheckListBox incluya algún texto como sugiere la imágen de tu primer mensaje?

Perdón por explicarme tan mal. Lo que quiero es que cuando haya un valor NULL en la columna literal, al cargar el CheckListBox, se asocie a Desconocida. Más que nada, y lo sabéis, si un usuario del programa ve una casilla sin descripción como en el ejemplo que subí al principio o que la etiqueta ponga NULO como sugieres, tardan 30 segundos en llamarte para preguntarte que es eso de NULO. Lo he solucionado así:
Código:

Query->Close();
Query->SQL->Text = "SELECT DISTINCT A.Situacion, "
                  "COALESCE(B.Literal, 'Desconocida') Literal "
                  "FROM Persona A "
                  "LEFT JOIN Instalacion B "
                  "ON A.Situacion = B.Valor AND B.Etiqueta = 'Situacion' "
                  "WHERE A.CodPrv = (SELECT Literal FROM Instalacion WHERE Etiqueta = 'Provincia')";
Query->Open();

bool lPrueba = false;
for (; !fPersona->Query->Eof; fPersona->Query->Next())
{
    if (UpperCase(fPersona->Query->FieldByName("Literal")->AsString) == "DESCONOCIDA")
    {
          if (lPrueba)
              continue;

          lPrueba = true;
    }
    CheckListBox2->Items->Add(fPersona->Query->FieldByName("Literal")->AsString);
}



La franja horaria es GMT +2. Ahora son las 23:22:46.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi