none
Consulta que parece fácil pero no lo es tanto RRS feed

  • Pregunta

  • Buenas, antes que nada avisar que estoy muy pero que MUY verde en SQL a pesar de haber leído y practicado mucho. Estoy tratando de diseñar una consulta y tras varias horas probando todo lo que se me ocurre estoy al borde del colapso, necesito ayuda. El caso es el siguiente, a ver si me podéis ayudar: tengo dos tablas, en una (CTA00) se almacenan cuentas contables con todas sus propiedades (código, nombre, tipo de cuenta, etc.) y en otra (TAS01) las cuentas que componen ciertas plantillas de asientos predefinidos por el usuario (por ejemplo, una factura generaría un debe en el cliente, un haber en el iva y otro en la cuenta de prestaciones de servicios). Ilustro con un par de fragmentos:

    Tipo de asiento:

    SELECT * FROM TAS01 where CODEMP = '1' and TIPAST='1'

    Resultado:

    1	1	1	1	7050001	H	0	1	0	1	CCCCCCCCCX	1	0	0	0
    5	1	1	2	4770001	H	0	0	0	0		0	0	0	1
    9	1	1	3	430	D	0	0	1	0		0	1	1	0

    Mediante un join intento que la quinta columna (7050001, etc) muestre el nombre de la cuenta contable, almacenado en la tabla CTA00:

    SELECT        TAS01.ID_TAS01, TAS01.CODEMP, TAS01.TIPAST, TAS01.NUMLIN, TAS01.CTACTB, TAS01.TIPAPT, TAS01.SOLFAC, TAS01.SOLBIM, TAS01.SOLFPG, TAS01.SOLCCS, 
                             TAS01.CTRCST, TAS01.CTAMUL, TAS01.CTAUNI, TAS01.ACIMPR, TAS01.ACCUOT, CTA00.ID_CTA00, CTA00.CODEMP AS Expr1, CTA00.CTACTB AS Expr2, 
                             CTA00.CONCEP, CTA00.TRATAM, CTA00.CTRCST AS Expr3, CTA00.STBLOQ
    FROM            TAS01 FULL OUTER JOIN
                             CTA00 ON TAS01.CTACTB = CTA00.CTACTB
    WHERE        (TAS01.CODEMP = '1') AND (TAS01.TIPAST = '1') AND (CTA00.CODEMP = '1')

    Hay casos en los que TAS01 no existe en CTA00 y debería devolver un valor NULL. Solo obtengo dos filas ya que la tercera, la 430, al no existir almacenada en CTA00, no devuelve ningún valor, cuando debería ser algo así como

    7050001 PRESTACIONES DE SERVICIOS

    4770001 IVA REPERCUTIDO

    430         NULL

    Si os sirve de orientación he encontrado una consulta que en FIREBIRD funciona perfectamente pero en SQL Server no.

    select a.*,(select b.concep from cta00 b where b.codemp=a.codemp and b.ctactb=a.ctactb 
    plan (b index (CTA00_CTACTB))) as nomcta from tas01 a 
    where a.codemp='1' and a.tipast='1' plan (a index (TAS01_TIPAST)) order by a.numlin
    
    He probado anidando consultas, con todos los tipos de join, etc. Añadir el 430 a la tabla CTA00 sería una solución pero chapucera, ya que la idea es que el usuario pueda trabajar con cualquier tipo de cuenta aunque no esté dada de alta. Cualquier ayuda será bien recibida. Gracias.

    martes, 15 de julio de 2014 15:58

Respuestas

  • Buenas de nuevo,

    Soy del género cabezón, finalmente lo he solucionado. Hunchback gracias por tus consejos pero la consulta que pusiste arrojaba más resultados de los necesarios. De hecho la cláusula CTA00.CODEMP es imprescindible ya que de lo contrario devolverá tantas filas como cuentas del mismo código que existan en cada empresa. Al final lo he resuelto mediante Full Join.

    El error radicaba en que al haber cuentas en la tabla TAS01 que no existían en la CTA00 y le introducía tres condiciones Where, dejaba de lado las filas NULL en la segunda tabla. Lo he solucionado añadiendo un paréntesis que aglutine el valor especificado por el usuario para la segunda tabla, más los valores NULL, quedando así:

    SELECT * FROM TAS01 full join CTA00 on CTA00.CTACTB = TAS01.CTACTB
    WHERE tas01.TIPAST='1' AND TAS01.CODEMP='1' AND (CTA00.CODEMP is NULL or cta00.CODEMP='1')
    ORDER BY TAS01.NUMLIN
    Ahora funciona exactamente como yo quería. Gracias por vuestra ayuda!

    • Marcado como respuesta Uriel Almendra miércoles, 16 de julio de 2014 15:30
    miércoles, 16 de julio de 2014 14:33

Todas las respuestas

  • Cuando haces un FULL JOIN se devolverá los datos que se crucen y los que no de cada tabla, revisaste que una de tus clausulas del WHERE no este excluyendo el registro que estas queriendo?
    martes, 15 de julio de 2014 16:05
  • estos where son imprescindibles ya que TAS01.CODEMP es el código de empresa y TAS01.TIPAST es el tipo de asiento. Sin hacer joins de ningún tipo devuelve las tres filas que corresponden. En el momento que trato de hacer el join con la tabla CTA00 para obtener el nombre de la cuenta, se reduce a dos. Es extraño desde luego, a juzgar por lo que me dices que coincide con toda la documentación consultada.
    martes, 15 de julio de 2014 16:52
  • No entiendo por que usas FULL OUTER JOIN ya que segun describes lo que necesitas es usar LEFT OUTER JOIN y ademas quitar el filtro " CTA00.CODEMP = '1' " en la clausula WHERE porque de lo contrario cambias la semantica de OUTER a INNER.

    SELECT
        TAS01.ID_TAS01,
        TAS01.CODEMP,
        TAS01.TIPAST,
        TAS01.NUMLIN,
        TAS01.CTACTB,
        TAS01.TIPAPT,
        TAS01.SOLFAC,
        TAS01.SOLBIM,
        TAS01.SOLFPG,
        TAS01.SOLCCS,
        TAS01.CTRCST,
        TAS01.CTAMUL,
        TAS01.CTAUNI,
        TAS01.ACIMPR,
        TAS01.ACCUOT,
        CTA00.ID_CTA00,
        CTA00.CODEMP AS Expr1,
        CTA00.CTACTB AS Expr2,
        CTA00.CONCEP,
        CTA00.TRATAM,
        CTA00.CTRCST AS Expr3,
        CTA00.STBLOQ
    FROM
        TAS01
        LEFT OUTER JOIN 
        CTA00
        ON TAS01.CTACTB = CTA00.CTACTB
    WHERE
        (TAS01.CODEMP = '1')
        AND (TAS01.TIPAST = '1');

    Cuando usamos un OUTER JOIN es porque queremos preservar las filas del lado en cuestion independiente de que exista alguna fila en el otro lado. En el caso de que no haya una fila del otro lado entonces las columnas del otro lado que referenciamos en la lista de la clausula SELECT contendran la marca NULL.

    En tu caso no existe una fila en CTA00 para el valor 430 de la columna CTACTB por lo que para esta fila en el resultado el valor de CTA00.CODEMP sera la marca NULL y por lo tanto tu la excluyes al usar el filtro antes mencionado.


    AMB

    Some guidelines for posting questions...


    martes, 15 de julio de 2014 17:32
  • Buenas de nuevo,

    Soy del género cabezón, finalmente lo he solucionado. Hunchback gracias por tus consejos pero la consulta que pusiste arrojaba más resultados de los necesarios. De hecho la cláusula CTA00.CODEMP es imprescindible ya que de lo contrario devolverá tantas filas como cuentas del mismo código que existan en cada empresa. Al final lo he resuelto mediante Full Join.

    El error radicaba en que al haber cuentas en la tabla TAS01 que no existían en la CTA00 y le introducía tres condiciones Where, dejaba de lado las filas NULL en la segunda tabla. Lo he solucionado añadiendo un paréntesis que aglutine el valor especificado por el usuario para la segunda tabla, más los valores NULL, quedando así:

    SELECT * FROM TAS01 full join CTA00 on CTA00.CTACTB = TAS01.CTACTB
    WHERE tas01.TIPAST='1' AND TAS01.CODEMP='1' AND (CTA00.CODEMP is NULL or cta00.CODEMP='1')
    ORDER BY TAS01.NUMLIN
    Ahora funciona exactamente como yo quería. Gracias por vuestra ayuda!

    • Marcado como respuesta Uriel Almendra miércoles, 16 de julio de 2014 15:30
    miércoles, 16 de julio de 2014 14:33
  • Tu conces tu data mejor que nosotros y ademas no tenemos acceso a ella para poder validar nuestras sugerencias.

    La sentencia que usas es equivalente a:

    SELECT
        * 
    FROM
        TAS01 
        INNER JOIN
        CTA00 
        ON CTA00.CTACTB = TAS01.CTACTB
    WHERE
        TAS01.TIPAST='1' 
        AND TAS01.CODEMP='1' 
        AND (CTA00.CODEMP IS NULL OR cta00.CODEMP='1')
    ORDER BY
        TAS01.NUMLIN;
    GO

    Como indique antes, al usar los filtros que tienes en la clausula WHERE cambias la semantica de FULL OUTER JOIN a INNER JOIN. El resultado de ambas sentencia debe ser el mismo.


    AMB

    Some guidelines for posting questions...

    miércoles, 16 de julio de 2014 15:09