Get column where relation is not NULL - mysql

I have a database which was migrated from SQL Server to MySQL. I had an existing query in SQL Server to get the value of a column where the relation is not null, I mean, if the relation between two tables is null then it means that there should be a relation with another table.
This is the query I was using:
SELECT C.expediente,
C.status,
Sum(M.monto) AS monto,
Sum(M.interes) AS interes,
Sum(M.iva) AS iva,
Sum(M.capital) AS capital,
M.fecha_mov,
AB.tipo_abono,
AB.id_deposito,
Isnull(Min(tg.nombre), Min(tp.nombcomp)) AS nombreGrupo
FROM movimientos AS M
JOIN acreditados AS A
ON A.id_acreditado = M.id_acreditado
JOIN creditos AS C
ON C.id_credito = A.id_credito
JOIN abonos AS AB
ON AB.id_movimiento = M.id_movimiento
OUTER apply (SELECT TOP 1 G.nombre
FROM grupos G
WHERE G.id_credito = C.id_credito) tg
OUTER apply (SELECT TOP 1 P.nombres + ' ' + P.apellido_paterno + ' '
+ P.apellido_materno AS NombComp
FROM personas P
WHERE A.id_persona = P.id_persona) tp
GROUP BY M.fecha_mov,
AB.tipo_abono,
AB.id_deposito,
C.expediente,
C.status
ORDER BY M.fecha_mov
But it seems that MySQL has no OUTER APPLY or ISNULL. How can I translate this query to MySQL?

You have a couple of issues to review:
1 - The APPLY operator is not supported in MySQL. However, for your given usage, it looks like you can probably just use a LEFT OUTER JOIN.
2 - MySQL does not support TOP. Instead, use LIMIT. However, I don't think you need it in this case since you are using MIN of each of those fields.
3 - To concatenate strings in MySQL, use CONCAT vs "+".
4 - Finally, I prefer using COALESCE in MySQL to check for NULLs. There are other options though.
So putting it all together, this should be close (untested):
SELECT C.expediente,
C.status,
Sum(M.monto) AS monto,
Sum(M.interes) AS interes,
Sum(M.iva) AS iva,
Sum(M.capital) AS capital,
M.fecha_mov,
AB.tipo_abono,
AB.id_deposito,
COALESCE(MIN(tg.nombre), MIN(tp.nombcomp)) AS nombreGrupo
FROM movimientos AS M
JOIN acreditados AS A
ON A.id_acreditado = M.id_acreditado
JOIN creditos AS C
ON C.id_credito = A.id_credito
JOIN abonos AS AB
ON AB.id_movimiento = M.id_movimiento
LEFT OUTER JOIN (
SELECT id_credito, nombre
FROM grupos
) tg ON tg.id_credito = C.id_credito
LEFT OUTER JOIN (
SELECT id_persona, CONCAT(nombres,' ',apellido_paterno,' ',apellido_materno) AS NombComp
FROM personas
) tp ON A.id_persona = tp.id_persona
GROUP BY M.fecha_mov,
AB.tipo_abono,
AB.id_deposito,
C.expediente,
C.status
ORDER BY M.fecha_mov
You may not need some of those fields in your GROUP BY -- just your DISTINCT fields needed.

I think this turns it into a valid MySQL query:
SELECT C.expediente, C.status, Sum(M.monto) AS monto, Sum(M.interes) as interes,
Sum(M.iva) as iva, Sum(M.capital) as capital,
M.fecha_mov, AB.tipo_abono, AB.id_deposito,
coalesce(Min(tg.nombre), Min(tp.nombcomp)) AS nombreGrupo
FROM movimientos M join
acreditados A
ON A.id_acreditado = M.id_acreditado join
creditos C
ON C.id_credito = A.id_credito join
abonos AB
ON AB.id_movimiento = M.id_movimiento join
(select g.id_credito, max(g.nombre) -- arbitrary nombre
from grupos g
group by g.id_credito
) tg
on tG.id_credito = C.id_credito join
(SELECT P.id_persona, concat_ws(' ', P.nombres, P.apellido_paterno, P.apellido_materno) AS NombComp
FROM personas P
group by p.id_persona
) tp
on A.id_persona = tP.id_persona
GROUP BY M.fecha_mov, AB.tipo_abono, AB.id_deposito, C.expediente, C.status
ORDER BY M.fecha_mov
I replaced the isnull() with coalesce() -- if you are going between databases, use standard SQL where possible. I replaced the + for concatenation with concat_ws(). Note there is a small difference . . . if one of the fields is NULL, then the MySQL value will treat it as ''.
I replaced the cross apply with an aggregate. You use top 1 without an order by, so this fetches an arbitrary row. So, this version arbitrarily chooses the maximum value.

Related

SQL Sentence help select multiple tables

I have this setence that select a Product NAME from my table Produto and select the quantity from other table named Contagens where the Produto.EANU = Contagens.PRODUTO but I also have some QTD that are related to EANP istead of EANU and I want to them be shown as ARM instead of QTD.
Is there any way that I can get those 2 sentences in one ?
SELECT Produto.NAME, IFNULL(Contagens.QTD ,0) as QTD FROM Produto
LEFT
OUTER JOIN Contagens ON Produto.EANU = Contagens.PRODUTO
ORDER BY NAME
SELECT Produto.NAME, IFNULL(Contagens.QTD ,0) as ARM FROM Produto
LEFT
OUTER JOIN Contagens ON Produto.EANP = Contagens.PRODUTO
ORDER BY NAME
Combine the conditions, and then use IF in the SELECT list to test which condition was matched.
SELECT p.Name, IF(p.EANU = c.PRODUTO, Contagens.QTD, 0) AS QTD, IF(p.EANP = c.PRODUTO, Contagents.QTD, 0) AS ARM
FROM Produto AS p
LEFT JOIN Contagens AS c ON c.PRODUTO IN (p.EANU, p.EANP)
ORDER BY p.Name
Just join them using aliases:
SELECT Produto.NAME,
IFNULL(EANU_Contagens.QTD ,0) as QTD,
IFNULL(EANP_Contagens.QTD ,0) as ARM
FROM Produto
LEFT OUTER JOIN Contagens AS EANU_Contagens ON Produto.EANU = EANU_Contagens.PRODUTO
LEFT OUTER JOIN Contagens AS EANP_Contagens ON Produto.EANP = EANP_Contagens.PRODUTO
ORDER BY NAME

Or operator returns more rows then expected mysql

I have a table with some items that are related to car models. Those items could have several categories. I have to select all of them that are related to cars and in all given categories categories for each item stored in sc_products table.
Here is my query:
SELECT
t15_catalogue_line.
T15_GROUP,
sc_products.product_code,
sc_products.unic, sc_products.name_ru, UPPER(TRIM(sc_products.brief_description_ru)) AS brief_description_ru, sc_products.suupplier, price.Price, sc_group_discounts.`action`, sc_group_discounts.procent, sc_products.productID, price.in_stock, price.supplier, t10_item.T10_ITEM, t10_item.unic, t10_item.T10_DESC, t10_item.T10_IMG, t10_item.T10_ITEM_GROUP, t10_item.T10_FIELD1, t10_item.T10_FIELD2, t10_item.T10_FIELD3, t10_item.T10_FIELD4, t10_item.T10_FIELD5, t10_item.T10_FIELD6, t10_item.T10_FIELD7, t10_item.T10_FIELD8, t10_item.T10_FIELD9, t10_item.T10_FIELD10, t10_item.T10_FIELD11, t14_item_fields.T14_ITEM_GROUP, t14_item_fields.T14_FIELD, t14_item_fields.T14_NAME, t14_item_fields.T14_UNIT, t14_item_fields.T14_SEARCH
FROM
sc_products
LEFT OUTER JOIN t15_catalogue_line ON (sc_products.unic = t15_catalogue_line.unic)
LEFT OUTER JOIN price ON (sc_products.unic = price.unic) AND (sc_products.suupplier = price.postavchik)
LEFT OUTER JOIN sc_group_discounts ON (sc_products.item_group = sc_group_discounts.item_group)
LEFT OUTER JOIN t10_item ON (sc_products.unic = t10_item.unic and sc_products.CatText=t10_item.CatText and sc_products.brief_description_ru=t10_item.brand)
LEFT OUTER JOIN t14_item_fields ON (t10_item.T10_ITEM_GROUP = t14_item_fields.T14_ITEM_GROUP)
WHERE
sc_products.CatText = 'bracke mechanism' or
sc_products.CatText='bracke montage ' or
sc_products.CatText='hydraulic repair ' AND
t15_catalogue_line.T15_CARTYPE = '30442' AND
t15_catalogue_line.T15_GROUP = '666' and
sc_products.unic is not null and
sc_products. unic!=''
GROUP BY sc_products.product_code,sc_products.brief_description_ru, sc_products.suupplier
ORDER BY ISNULL( price.price),price.price ASC
Query returns all items in those caegories and they aren't related to auto so table t15_catalog line doesn't take part in query what could be the solution
I highly suspect that the missing parentheses () around the multiple AND is your problem.
Query returns all items in those caegories and they aren't related to auto so table t15_catalog line doesn't take part in query what could be the solution
Your grouping logic now does exactly what you describe, below is what i think you meant to do:
WHERE (sc_products.CatText = 'bracke mechanism' or sc_products.CatText='bracke montage ' or sc_products.CatText='hydraulic repair ') AND t15_catalogue_line.T15_CARTYPE = '30442' AND t15_catalogue_line.T15_GROUP = '666' and sc_products.unic is not null and sc_products. unic!='' GROUP BY sc_products.product_code,sc_products.brief_description_ru, sc_products.suupplier ORDER BY ISNULL( price.price),price.price ASC
This logic:
a OR b OR c AND d is equivalent to a OR b OR (c AND d) and to not (a OR b OR c) AND d which seems to be what you want.
You need to add parentheses around the OR block like this:
WHERE
(
sc_products.CatText = 'bracke mechanism'
OR sc_products.CatText = 'bracke montage '
OR sc_products.CatText = 'hydraulic repair '
)
AND t15_catalogue_line.T15_CARTYPE = '30442'
AND t15_catalogue_line.T15_GROUP = '666'
AND sc_products.unic is not null
AND sc_products. unic!=''
You probably want to use paranthesis in your query for the desired result. How about
SELECT
t15_catalogue_line.
T15_GROUP,
sc_products.product_code,
sc_products.unic, sc_products.name_ru,
UPPER(TRIM (sc_products.brief_description_ru)) AS brief_description_ru,
sc_products.suupplier, price.Price, sc_group_discounts.`action`,
sc_group_discounts.procent, sc_products.productID,
price.in_stock, price.supplier, t10_item.T10_ITEM, t10_item.unic,
t10_item.T10_DESC, t10_item.T10_IMG, t10_item.T10_ITEM_GROUP,
t10_item.T10_FIELD1, t10_item.T10_FIELD2, t10_item.T10_FIELD3,
t10_item.T10_FIELD4, t10_item.T10_FIELD5, t10_item.T10_FIELD6,
t10_item.T10_FIELD7, t10_item.T10_FIELD8, t10_item.T10_FIELD9,
t10_item.T10_FIELD10, t10_item.T10_FIELD11,
t14_item_fields.T14_ITEM_GROUP, t14_item_fields.T14_FIELD,
t14_item_fields.T14_NAME, t14_item_fields.T14_UNIT,
t14_item_fields.T14_SEARCH
FROM
sc_products
LEFT OUTER JOIN t15_catalogue_line ON
(sc_products.unic = t15_catalogue_line.unic)
LEFT OUTER JOIN price ON
(sc_products.unic = price.unic)
AND (sc_products.suupplier = price.postavchik)
LEFT OUTER JOIN sc_group_discounts ON
(sc_products.item_group = sc_group_discounts.item_group)
LEFT OUTER JOIN t10_item ON
(sc_products.unic = t10_item.unic and
sc_products.CatText=t10_item.CatText and
sc_products.brief_description_ru=t10_item.brand)
LEFT OUTER JOIN t14_item_fields ON
(t10_item.T10_ITEM_GROUP = t14_item_fields.T14_ITEM_GROUP)
WHERE
(
sc_products.CatText = 'bracke mechanism'
or
sc_products.CatText='bracke montage '
or
sc_products.CatText='hydraulic repair '
)
AND t15_catalogue_line.T15_CARTYPE = '30442'
AND t15_catalogue_line.T15_GROUP = '666'
and sc_products.unic is not null
and sc_products. unic!=''
GROUP BY
sc_products.product_code,sc_products.brief_description_ru,
sc_products.suupplier ORDER BY ISNULL( price.price),
price.price ASC

ordering 2 tables by date

I want to order 2 tables by date, but the problem is sql is not ordering them simultaneously.
here is my query:
SELECT users_id,CONCAT_WS(' ', users_fname, users_lname)
AS full_name, reply_message, concern_message
FROM tbl_usersinfo AS i
LEFT JOIN tbl_concern AS c
ON c.student_id = i.users_id
LEFT JOIN tbl_reply_concern AS r
ON r.student_id = i.users_id
ORDER BY c.date,r.date
I read that i need to put ISNULL(c.date,r.date) but its not working.
Try ifnull or coalesce
SELECT users_id,
CONCAT_WS(' ', users_fname, users_lname) AS full_name,
reply_message,
concern_message
FROM tbl_usersinfo AS i
LEFT JOIN tbl_concern AS c
ON c.student_id = i.users_id
LEFT JOIN tbl_reply_concern AS r
ON r.student_id = i.users_id
ORDER BY ifnull(c.date, r.date)
The MySQL equivalent of ISNULL (in SQL Server) is IFNULL. In MySQL I believe ISNULL is just a test of whether something is or is not null which would evaluate to 0 or 1.

mysql composite if column from composite values?

I need to add an extra column to my query results,
The column needs to be called percent,
If col1 == 0 then percent should contain NIS else percent should contain ceil(col2 / col1 * 100)
So I believe the following should work:
IF(col1 = 0, 'NIS', ceil(col2 / col1 * 100)) as percent
But I run into an issue as col1 and col2 are also composite's.
COUNT(distinct i.id) as col1
COUNT(distinct q.id) as col2
So I get hit with
Unknown column 'col1' in 'field list'
I could get around that issue with
IF(COUNT(distinct i.id) = 0, 'NIS', ceil(COUNT(distinct q.id) / COUNT(distinct i.id) * 100)) as percent
But that just seems like a bunch of extra processing to me, surely there is a better way around that?
Full Query
SELECT
`t`.*,
`r`.`name` AS region_name,
GROUP_CONCAT(DISTINCT p.ptype_id SEPARATOR "|") AS ptype_ids,
COUNT(DISTINCT q.id) AS quoted,
COUNT(DISTINCT i.id) AS intended,
COUNT(DISTINCT qa.id) AS awarded,
IF(
intended = 0,
`"NIS"`,
CEIL(quoted / intended * 100)
) AS percent
FROM
(`tradesmen` t)
LEFT JOIN `regions` r
ON `r`.`id` = `t`.`region_id`
LEFT JOIN `quotes` q
ON `t`.`id` = `q`.`tradesman_id`
LEFT JOIN `quote_intentions` i
ON `t`.`id` = `i`.`tradesman_id`
LEFT JOIN `quotes` qa
ON `q`.`tradesman_id` = `qa`.`tradesman_id`
AND qa.accepted = 1
LEFT JOIN `ptypes_tradesmen` p
ON `p`.`tradesman_id` = `t`.`id`
GROUP BY `t`.`id`
LIMIT 20
As mentioned in SELECT Syntax:
It is not permissible to refer to a column alias in a WHERE clause, because the column value might not yet be determined when the WHERE clause is executed. See Section C.5.5.4, “Problems with Column Aliases”.
Whilst the manual doesn't explicitly say so, the same reasoning applies to referring to a column alias within a select_expr.
You could place your query in a subquery to an outer one that calculates percent using the column aliases:
SELECT *, IF(intended, CEIL(quoted / intended * 100), NULL) AS percent FROM (
SELECT
`t`.*,
`r`.`name` AS region_name,
GROUP_CONCAT(DISTINCT p.ptype_id SEPARATOR "|") AS ptype_ids,
COUNT(DISTINCT q.id) AS quoted,
COUNT(DISTINCT i.id) AS intended,
COUNT(DISTINCT qa.id) AS awarded
FROM
(`tradesmen` t)
LEFT JOIN `regions` r
ON `r`.`id` = `t`.`region_id`
LEFT JOIN `quotes` q
ON `t`.`id` = `q`.`tradesman_id`
LEFT JOIN `quote_intentions` i
ON `t`.`id` = `i`.`tradesman_id`
LEFT JOIN `quotes` qa
ON `q`.`tradesman_id` = `qa`.`tradesman_id`
AND qa.accepted = 1
LEFT JOIN `ptypes_tradesmen` p
ON `p`.`tradesman_id` = `t`.`id`
GROUP BY `t`.`id`
LIMIT 20
) t
However, I don't think this is really worthwhile as I believe (am looking for a reference that I can cite, but nothing forthcoming yet) MySQL will only calculate each COUNT() once and use the cached result in each reference.

Multiple many-to-many JOINs in a single mysql query without Cartesian Product

At the moment I can get the results I need with two seperate SELECT statements
SELECT
COUNT(rl.refBiblioID)
FROM biblioList bl
LEFT JOIN refList rl ON bl.biblioID = rl.biblioID
GROUP BY bl.biblioID
SELECT
GROUP_CONCAT(
CONCAT_WS( ':', al.lastName, al.firstName )
ORDER BY al.authorID )
FROM biblioList bl
LEFT JOIN biblio_author ba ON ba.biblioID = bl.biblioID
JOIN authorList al ON al.authorID = ba.authorID
GROUP BY bl.biblioID
Combining them like this however
SELECT
GROUP_CONCAT(
CONCAT_WS( ':', al.lastName, al.firstName )
ORDER BY al.authorID ),
COUNT(rl.refBiblioID)
FROM biblioList bl
LEFT JOIN biblio_author ba ON ba.biblioID = bl.biblioID
JOIN authorList al ON al.authorID = ba.authorID
LEFT JOIN refList rl ON bl.biblioID = rl.biblioID
GROUP BY bl.biblioID
causes the author result column to have duplicate names. How can I get the desired results from one SELECT statement without using DISTINCT? With subqueries?
If subselects are acceptable, I believe you could make this happen by doing something like this:
SELECT
y.authors,
COUNT(rl.refBiblioID)
FROM
(SELECT
bl.biblioId,
GROUP_CONCAT(
CONCAT_WS(':', al.lastName, al.firstName)
ORDER BY al.authorID) AS authors
FROM biblioList bl
LEFT JOIN biblio_author ba ON ba.biblioID = bl.biblioID
JOIN authorList al ON al.authorID = ba.authorID
GROUP BY bl.biblioID) y
LEFT JOIN refList rl ON (y.biblioID = rl.biblioID)
GROUP BY y.biblioID
Another solution would be to add DISTINCT to your GROUP_CONCAT, but that was not what you wanted?
GROUP_CONCAT(
DISTINCT(CONCAT_WS(':', al.lastName, al.firstName) ORDER BY al.authorID)),