Mysql: Finding parent of multiple childrens - mysql

I have 2 tables: players and items. Now I'm looking for player who has item with known properties ex.
SELECT players.`name` FROM `players`
INNER JOIN `items` ON players.`id`=items.`ownerId`
WHERE items.`itemType` = 1 AND items.`itemClass` = 2 AND items.`itemColor` = 3
How i can find player which has more than one item i want? It is even possible in one query?
Ex. i wanna find player which has both items : type=1 class=2 color=3 , type=2 class=3 color=4
I have an idea how to do it in multiple querys: just add players.id IN (...) on every next query.
Thanks for all your help!

The best way to do this is with aggregation.
select i.ownerId
from Items i
group by i.ownerId
having max(case when i.itemType = 1 and i.itemClass = 2 and i.itemColor = 3
then 1 else 0
end) = 1 and
max(case when i.itemType = 2 and i.itemClass = 3 and i.itemColor = 4
then 1 else 0
end) = 1
If you want other information about the owner, you need to join in the players table.
In MySQL, you can simplify the having clause to:
having max(i.itemType = 1 and i.itemClass = 2 and i.itemColor = 3) = 1 and
max(i.itemType = 2 and i.itemClass = 3 and i.itemColor = 4) = 1

Related

Select Case inside another Select

I’m trying to apply a CASE to a column that I create.
But the problem is that I have a select inside another select
And I’m using the value of that subquery to make the select case
How should I do this?
SELECT
gc.IDGastosComunes,
v.IDPropiedad,
v.Depto,
v.NombreDueno,
(SELECT u2.IDCategoria
FROM usuarios_tipos u2
WHERE u2.IDUsuario = v.IDDueno AND
u2.IDTipo = 10) AS gc.IDCategoria, <-- This return an int -> 1, 2 or 3
CASE
WHEN IDCategoria = 1 THEN 'Convenio'
WHEN IDCategoria = 2 THEN 'Plus'
WHEN IDCategoria = 3 THEN 'Preferente'
WHEN IDCategoria = 4 THEN 'Premium'
WHEN IDCategoria = 5 THEN 'La Serena'
ELSE ''
END AS Categoria, <-- This has to show the string
gc.monto,
gc.FechaEmisionPago AS Periodo
FROM
gastos_comunes gc
JOIN vcartera_propiedades v ON (v.IDPropiedad=gc.IDPropiedad)
WHERE v.IDGrupo = 1
You would typically move the case expression inside the subquery:
SELECT
gc.IDGastosComunes,
v.IDPropiedad,
v.Depto,
v.NombreDueno,
(
SELECT CASE u2.IDCategoria
WHEN 1 THEN 'Convenio'
WHEN 2 THEN 'Plus'
WHEN 3 THEN 'Preferente'
WHEN 4 THEN 'Premium'
WHEN 5 THEN 'La Serena'
ELSE ''
END
FROM usuarios_tipos u2
WHERE u2.IDUsuario = v.IDDueno AND u2.IDTipo = 10
) AS Categoria
gc.monto,
gc.FechaEmisionPago AS Periodo
FROM gastos_comunes gc
JOIN vcartera_propiedades v ON v.IDPropiedad=gc.IDPropiedad
WHERE v.IDGrupo = 1
Side note: this would need to be tested, but likely, your query can be rewritten to use a (LEFT) JOIN instead of an inline subquery, which would make the syntax much shorter. That would look like:
SELECT
gc.IDGastosComunes,
v.IDPropiedad,
v.Depto,
v.NombreDueno,
CASE u2.IDCategoria
WHEN 1 THEN 'Convenio'
WHEN 2 THEN 'Plus'
WHEN 3 THEN 'Preferente'
WHEN 4 THEN 'Premium'
WHEN 5 THEN 'La Serena'
ELSE ''
END AS Categoria
gc.monto,
gc.FechaEmisionPago AS Periodo
FROM gastos_comunes gc
JOIN vcartera_propiedades v ON v.IDPropiedad=gc.IDPropiedad
LEFT JOIN usuarios_tipos u2 ON u2.IDUsuario = v.IDDueno AND u2.IDTipo = 10
WHERE v.IDGrupo = 1

How to display mySQL rows even if there are not SUM results?

I am trying to display a report from mySQL. Here is my current query:
SELECT *,
Sum(CASE
WHEN alerts_data_status = 'goal' THEN 1
ELSE 0
END) AS goal,
Sum(CASE
WHEN alerts_data_status = 'delivered' THEN 1
ELSE 0
END) AS delivered,
Sum(CASE
WHEN alerts_data_status = 'closed' THEN 1
ELSE 0
END) AS closed
FROM alerts_data
WHERE alerts_data.company_id = 1
GROUP BY alerts_data.alerts_data_id
the thing is that if a alerts_data.id has 0 goal, 0 delivered, 0 closed, it won't be shown in the results. The query shows only the alerts_data.id with at least 1 goal or 1 delivered or 1 closed.
How can I achieve this?
Example output
company ---- id --- goal --- delivered --- closed
1 ---- 32 --- 1 ------ 4 ----- 10
1 ---- 11 --- 0 ------ 1 ----- 1
Thank you
I think the issue that you are having is that there are no rows in the table for the company. Use an aggregation query with no GROUP BY:
SELECT 1 as company_id,
COALESCE(SUM(alerts_data_status = 'goal'), 0) AS goal,
COALESCE(SUM(alerts_data_status = 'delivered'), 0) AS delivered,
COALESCE(SUM(alerts_data_status = 'closed'), 0) AS closed
FROM alerts_data ad
WHERE ad.company_id = 1;
This no GROUP BY, this is guaranteed to return one row -- even if the WHERE clause filters out all rows. A GROUP BY returns one row per group, so if all rows are filtered out, then there are no groups and no rows in the result set.
If you wanted to support multiple company ids, you could use a LEFT JOIN:
SELECT company_id,
COALESCE(SUM(alerts_data_status = 'goal'), 0) AS goal,
COALESCE(SUM(alerts_data_status = 'delivered'), 0) AS delivered,
COALESCE(SUM(alerts_data_status = 'closed'), 0) AS closed
FROM (SELECT 1 as company_id UNION ALL
SELECT 2 as company_id
) c LEFT JOIN
alerts_data ad
USING (company_id)
GROUP BY company_id;
The LEFT JOIN guarantees that there are rows for each company, so each will be in the result set.
You can also phrase this as:
SELECT 1 as company_id,
COALESCE(SUM(alerts_data_status = 'goal'), 0) AS goal,
COALESCE(SUM(alerts_data_status = 'delivered'), 0) AS delivered,
COALESCE(SUM(alerts_data_status = 'closed'), 0) AS closed
FROM alerts_data ad
WHERE ad.company_id = 1;
This no GROUP BY, this is guaranteed to return one row -- even if the WHERE clause filters out all rows. A GROUP BY returns one row per group, so if all rows are filtered out, then there are no groups and no rows in the result set.
If you wanted to support multiple company ids, you could use a LEFT JOIN:
SELECT c.company_id,
COALESCE(SUM(ad.alerts_data_status = 'goal'), 0) AS goal,
COALESCE(SUM(ad.alerts_data_status = 'delivered'), 0) AS delivered,
COALESCE(SUM(ad.alerts_data_status = 'closed'), 0) AS closed
FROM companies c LEFT JOIN
alerts_data ad
on c.company_id = ad.company_id
WHERE c.company_id IN (1) -- or a longer list
GROUP BY c.company_id;
You need a LEFT JOIN of alerts_list to alerts_data:
SELECT t.alerts_id,
SUM(a.alerts_data_status = 'goal') AS goal,
SUM(a.alerts_data_status = 'delivered') AS delivered,
SUM(a.alerts_data_status = 'closed') AS closed
FROM alerts_list AS t LEFT JOIN alerts_data AS a
ON a.alerts_data_id = t.alerts_id AND a.company_id = t.company_id
WHERE t.company_id = 1
GROUP BY t.alerts_data_id

Conditional condition in ON clause

I am trying to apply a conditional condition inside ON clause of a LEFT JOIN. What I am trying to achieve is somewhat like this:
Pseudo Code
SELECT * FROM item AS i
LEFT JOIN sales AS s ON i.sku = s.item_no
AND (some condition)
AND (
IF (s.type = 0 AND s.code = 'me')
ELSEIF (s.type = 1 AND s.code = 'my-group')
ELSEIF (s.type = 2)
)
I want the query to return the row, if it matches any one of the conditions (Edit: and if it matches one, should omit the rest for the same item).
Sample Data
Sales
item_no | type | code | price
1 0 me 10
1 1 my-group 12
1 2 14
2 1 my-group 20
2 2 22
3 2 30
4 0 not-me 40
I want the query to return
item_no | type | code | price
1 0 me 10
2 1 my-group 20
3 2 30
Edit: The sales is table is used to apply special prices for individual users, user groups, and/or all users.
if type = 0, code contains username. (for a single user)
if type = 1, code contains user-group. (for users in a group)
if type = 2, code contains empty-string (for all users).
Use the following SQL (assumed, the the table sales has a unique id field as usual in yii):
SELECT * FROM item AS i
LEFT JOIN sales AS s ON i.sku = s.item_no
AND id = (
SELECT id FROM sales
WHERE item_no = i.sku
AND (type = 0 AND code = 'me' OR
type = 1 AND code = 'my-group' OR
type = 2)
ORDER BY type
LIMIT 1
)
Try following -
SELECT *,SUBSTRING_INDEX(GROUP_CONCAT(s.type ORDER BY s.type),','1) AS `type`, SUBSTRING_INDEX(GROUP_CONCAT(s.code ORDER BY s.type),','1) AS `code`,SUBSTRING_INDEX(GROUP_CONCAT(s.price ORDER BY s.type),','1) AS `price`
FROM item AS i
LEFT JOIN sales AS s
ON i.sku = s.item_no AND (SOME CONDITION)
GROUP BY i.sku

Mysql query from one table with different conditions

I would like to calculate every item's vote count for this table:
user item vote
--------------------------------
4 1 left
4 2 left
2 2 right
3 2 left
1 3 right
The result must be like this:
item | left_vote | right_vote
1 1 0
2 2 1
3 0 1
I've tried to use queries like this:
SELECT t.item, count(vote) as left_count, t.right_count from (SELECT count(vote) as right_count, item from view where vote = 'right') as t, view where vote = 'left';
But it doesn't work. I think that I have to use join with subquery.
Is it real with mysql?
In MySQL you can just use conditional aggregation:
select item, sum(vote = 'left') as left_vote, sum(vote = 'right') as right_vote
from votes v
group by item;
You don't need a join or subquery for this.

MySql, inner join query, must match multiple rows

I am making a MySQL query where I want to retrieve an ID but only if I find a match for it in all the rows which I specify in the query.
Table: view_layout_rows
ID owner rows
___________________
49 1 2
50 1 2
Table: view_layout_rows_columns
ID row columns
___________________
49 1 5
49 2 4
50 1 5
50 2 5
SELECT vlr.id
FROM view_layout_rows vlr
INNER JOIN view_layout_rows_columns vlrc
ON vlr.id = vlrc.id
WHERE vlr.rows = 2
AND (vlr.owner = 0 OR vlr.owner = 1)
AND all of the following conditions should be satisfied:
(vlrc.row = 1 AND vlrc.columns = 5)
(vlrc.row = 2 AND vlrc.columns = 5)
Only ID 50 should be returned. 49 should NOT be returned as it only satisfies the first of the final two clauses.
How might I go about this?
(Please note, I asked this question previously but my requirement was unclear. Second attempt.)
Thanks in advance for any suggestions.
Double join to the rescue! :-)
SELECT vlc.*
FROM view_layout_rows vlc
INNER JOIN view_layout_rows_columns vlrc1 ON vlrc1.id = vlc.id
INNER JOIN view_layout_rows_columns vlrc2 ON vlrc2.id = vlc.id
WHERE vlrc1.row = 1 AND vlrc1.columns = 5
AND vlrc2.row = 2 AND vlrc2.columns = 5
/* imported from original query */
AND vlr.rows = 2
AND (vlr.owner = 0 OR vlr.owner = 1);
Single access of each table:
select r.*
from view_layout_rows r
join (select id, count(*) rec_count
from view_layout_rows_columns
where row in (1,2) and
columns = 5
group by id
having count(*) = 2) c
on r.id = c.id
where r.rows = 2 and
r.owner in (0,1)