Consulta MySQL que devuelva 0

eXtreM3

Buenas, tengo una duda y no sé si se puede hacer directamente con la consulta.

Pongamos que tengo las tablas Tickets y Vendedores. Un vendedor puede vender uno o muchos tickets.

vendedor1 -> 5 tickets
vendedor2 -> 15 tickets
vendedor3 -> 0 tickets
vendedor4 -> 3 tickets
vendedor5 -> 0 tickets

Si hago una consulta que me devuelva el COUNT de tickets que ha vendido cada vendedor (relacionados por el ID del vendedor, que está en ambas tablas) me devolvería estos 3 registros:

vendedor1 -> 5 tickets
vendedor2 -> 15 tickets
vendedor4 -> 3 tickets

¿es posible que devuelva también los que han vendido 0?

CCiRviNe

Si las tablas son algo así:
vendedor: vendedor_id, vendedor_nombre (...)
ticket: ticket_id, vendedor_id (...)

Haciendo un LEFT JOIN de vendedor con ticket y haciendo un COUNT(ticket_id) con GROUP BY vendedor_id debería darte lo que quieres.

SELECT vendedor_nombre, COUNT(ticket_id) AS vendedor_venta_tickets
FROM vendedor LEFT JOIN ticket USING(vendedor_id)
GROUP BY vendedor_id

El LEFT JOIN nos devuelve los resultados de la tabla de vendedores siempre, aunque no tenga valores en la tabla relacionada. El COUNT(ticket_id) nos devuelve la cantidad de registros que existen en la tabla de tickets para cada registro de la tabla vendedor.

1 respuesta
eXtreM3

#2 sí, más o menos son así las tablas. Copio la consulta tal y como la tengo (quitando varios parámetros del where que no influyen):

SELECT
COUNT(ticket_description.units) as totalEntries,
vendors.vendorName,
vendors.vendorId
FROM ( tickets, tpv, vendors, ticket_description )
WHERE tickets.ticketId = ticket_description.ticketId 
AND tickets.tpvId = tpv.tpvId 
AND tickets.vendorId = vendors.vendorId 
AND (vendors.vendorId = 25 OR vendors.vendorId = 9 OR vendors.vendorId = 24 OR vendors.vendorId = 29 OR vendors.vendorId = 27) 
GROUP BY vendorName 
ORDER BY vendorName ASC

Esto me devuelve correctamente datos siempre y cuando el vendedor haya realizado alguna venta de tickets, en mi caso 3 de los 5 ids que hay.

Supuestamente lo que me comentas del LEFT JOIN es lo mismo que yo hago con tickets.vendorId = vendors.vendorId para relacionar ambas tablas, ¿no?

1 respuesta
CCiRviNe

#3 no exactamente. El WHERE es más bien un INNER JOIN, es decir, sólo saca las filas que cumplen la condición de que exista una fila en la tabla tickets que tenga el identificador de un vendedor de la tabla vendors. Con el LEFT JOIN conseguimos todas las filas de la tabla vendors, tengan tickets o no. Si no tienen tickets el campo de tickets sería NULL.

Pic related (piensa que la A es vendor y la B es ticket)

4 2 respuestas
eXtreM3

#4 cómo se transforma exactamente la sintaxis? He probado esto y me devuelve exactamente lo mismo

SELECT
COUNT(ticket_description.units) as totalEntries,
vendors.vendorName,
vendors.vendorId
FROM (  tpv, vendors, ticket_description )
LEFT JOIN tickets ON (vendors.vendorId = tickets.vendorId)
WHERE tickets.ticketId = ticket_description.ticketId 
AND tickets.tpvId = tpv.tpvId 
AND (vendors.vendorId = 25 OR vendors.vendorId = 9 OR vendors.vendorId = 24 OR vendors.vendorId = 29 OR vendors.vendorId = 27) 
GROUP BY vendorName 
ORDER BY vendorName ASC

Con el USING que tú has comentado me tira un error de sintaxis la consulta :\

1 respuesta
CCiRviNe

#5 el USING puede ser porque haya más de 2 tablas con un campo vendorId?

Si no me equivoco sería algo así

SELECT COUNT(ticket_description.units) as totalEntries,
vendors.vendorName,
vendors.vendorId
FROM vendors 
    LEFT JOIN tickets ON vendors.vendorId = tickets.vendorId
    LEFT JOIN tpv ON tpv.tpvId = tickets.tpvId
    LEFT JOIN ticket_description ON tickets.ticketId = ticket_description.ticketId
WHERE vendors.vendorId IN (25,9,24,29,27)
GROUP BY vendorName
ORDER BY vendorName ASC
1 respuesta
KoRMuZ

#4 te amo por la imagen explicando los join. te amo mucho

1 respuesta
eXtreM3

#6 esa consulta devuelve lo mismo que la mía de arriba, joderrrrr xD

pd: a mí también me ha gustao la pic de los join, aunque de momento sude de nosotros el left join ¬¬

1 respuesta
CCiRviNe

#7 si es que es la caña, no sé por qué no la usan cuando dan clases de bases de datos en vez de usar simbolitos raros :D
#8 si puedes poner el resultado de la consulta y algunos datos que tengas, igual es por alguna otra cosa. A no ser que sea porque he agrupado por vendorName en lugar de vendorId y tengas vendedores distintos con el mismo nombre xD

1 respuesta
eXtreM3

#9 allá voy.

Consulta 1: rango de fechas más amplio, devuelve 5 registros

SELECT COUNT(ticket_description.units) as totalEntries,
vendors.vendorName,
vendors.vendorId
FROM vendors 
    LEFT JOIN tickets ON vendors.vendorId = tickets.vendorId
    LEFT JOIN tpv ON tpv.tpvId = tickets.tpvId
    LEFT JOIN ticket_description ON tickets.ticketId = ticket_description.ticketId
WHERE vendors.vendorId IN (25,9,24,29,27)
AND ticketDate BETWEEN '2013-08-01' AND '2014-04-30'
GROUP BY vendorId
ORDER BY vendorId ASC

Pic:

Consulta 2: rango de fechas más corto, devuelve 3 registros

SELECT COUNT(ticket_description.units) as totalEntries,
vendors.vendorName,
vendors.vendorId
FROM vendors 
    LEFT JOIN tickets ON vendors.vendorId = tickets.vendorId
    LEFT JOIN tpv ON tpv.tpvId = tickets.tpvId
    LEFT JOIN ticket_description ON tickets.ticketId = ticket_description.ticketId
WHERE vendors.vendorId IN (25,9,24,29,27)
AND ticketDate BETWEEN '2014-04-01' AND '2014-04-30'
GROUP BY vendorId
ORDER BY vendorId ASC

Pic:

Supuestamente en la consulta en la que sólo pido los tickets de abril (la segunda) también deberían aparecer los 2 vendedores que no han vendido nada en ese mes, los cuales sí aparecen en la consulta 1 porque en ese rango sí que han vendido.

Vaya tostón te estoy dando, y gracias por seguir aquí!

2 respuestas
CCiRviNe

#10 vale, la clave está en el filtro de fechas.

Prueba a cambiar:

AND ticketDate BETWEEN '2014-04-01' AND '2014-04-30'

por

AND (ticketDate BETWEEN '2014-04-01' AND '2014-04-30' OR ticketDate IS NULL)

¿puede haber tickets cuya ticketDate sea NULL? Ya que null es el valor que coge el campo al hacer left join

1 respuesta
eXtreM3

#11 nada, lo mismo. Y no, todos tienen fecha, nunca está a null.

¿Estás seguro de que se puede? Me estoy rallando más de la cuenta con esto :(, lo hago por php y que le follen.

1 respuesta
CCiRviNe

#12 100% seguro.

Haz una prueba: añade un vendor nuevo con vendorId 100 por ejemplo

Añade el 100 en el IN:
WHERE vendors.vendorId IN (25,9,24,29,27,100)

Y quita el filtro de fecha, es decir, deja solo el where vendor in a ver si te deuvelve un 0

1 respuesta
eXtreM3

#13 lo he hecho, al hacer la consulta sin la fecha aparecen los 5 de antes más este nuevo con un 0 de tickets vendidos.

1 respuesta
CCiRviNe

#14 otra prueba más a ver si esto funciona.

En lugar de hacer en el WHERE la consulta de fecha ponlo junto a la tabla en el LEFT JOIN, suponiendo que sea en la tabla tickets donde está el ticketDate sería:

SELECT COUNT(ticket_description.units) as totalEntries,
vendors.vendorName,
vendors.vendorId
FROM vendors 
    LEFT JOIN tickets ON vendors.vendorId = tickets.vendorId AND ticketDate BETWEEN '2014-04-01' AND '2014-04-30'
    LEFT JOIN tpv ON tpv.tpvId = tickets.tpvId
    LEFT JOIN ticket_description ON tickets.ticketId = ticket_description.ticketId
WHERE vendors.vendorId IN (25,9,24,29,27)
GROUP BY vendorId
ORDER BY vendorId ASC
1 1 respuesta
eXtreM3

#15 esa siiiiiiiiiiiiiiiii

Joder muchísimas gracias, en serio ;)

Dadle CT a este hombre!

1 respuesta
CCiRviNe

#16 nada hombre, me ha venido bien para hacer tiempo hasta la hora del poteo jaja.

Ahora me entra la duda de por qué no funciona la solución de #11 si creo que es correcta también jaja, otro día le daré una vuelta :P

1 respuesta
eXtreM3

#17 después de seguir toqueteando un poco la consulta, y otras parecidas que he tenido que realizar, he llegado a la conclusión de que tus primeras soluciones no valían porque el WHERE le mete prioridad máxima a las consultas, es decir:

Primero estábamos intentando coger todos los tickets vendidos por cualquier vendedor, aunque fueran 0, pero esto no era posible ya que directamente en el WHERE se estaba comiendo todos los que no estuvieran en el rango de fechas.

Las siguientes consultas que he tenido que hacer, simplemente se acota bien si indicamos todas las condiciones que queramos al mismo nivel que los LEFT JOIN, así no "machaca" con el WHERE, sino que añade las condiciones antes del filtrado.

;)

1 respuesta
CCiRviNe

#18 yep, es lo que te comentaba en #11, ya que hasta la respuesta #10 no me habías puesto el filtro de fechas, por eso te decía ahí lo de poner IS NULL ya que el LEFT JOIN nos devolvería valores de fecha NULL en el caso de que no exista ningún ticket para el vendedor, sin embargo no funcionaba y no sé por qué :(

1 respuesta
eXtreM3

#19 porque nunca llegaba a coger fechas NULL, al estar el filtro en el where, como te digo, hace eso antes que nada, y ya es imposible que recoja el valor de algún vendedor porque obviamente no está en esa lista después de hacerle el left join.

1 respuesta
CCiRviNe

#20 por eso el filtro era:
AND (ticketDate BETWEEN '2014-04-01' AND '2014-04-30' OR ticketDate IS NULL)
así aplicas el filtro que le digas: AND (si es una fecha de abril O es null) el paréntesis indica que cumpla una de esas dos opciones

Ninja edit:

Acabo de probar y me funciona xD

Usuarios habituales

  • CCiRviNe
  • eXtreM3
  • KoRMuZ