Mysql - choose main item in the Group By - mysql

I have the following query:
SELECT * FROM (
SELECT codigo, protocolo, status, nome
FROM protocolo
GROUP BY protocolo.protocolo
UNION ALL
SELECT codigo, protocolo, status, nome
FROM simulador
) tabela
return
codigo protocolo status nome
559 2016000026 1 ALESSANDRO CAMPOS BONIFACIO
0 2016000026 0 ALESSANDRO CAMPOS BONIFACIO
0 2016000008 0 MARIA DE JESUS F. DA SILVA ***
0 2016000007 0 MARGARIDA BORGES DA SILVA
558 2016000008 1 MARIA DE JESUS F. DA SILVA ***
556 2015014035 1 MARIA DALVA DA SILVA
There are two identical protocolo (2016000008) with different status (0,1) . I want to display only one of the repeated protocolo , one that has status = 1

Is this what you want?
SELECT odigo, protocolo, MAX(status) as stat, nome
FROM (
SELECT codigo, protocolo, status, nome
FROM protocolo
GROUP BY protocolo.protocolo
UNION ALL
SELECT codigo, protocolo, status, nome
FROM simulador
) tabela
GROUP BY codigo, protocolo, nome ;
Note: In a GROUP BY query, all columns in the SELECT should be either in the GROUP BY or in aggregation functions, unless you really, really know what you are doing.

Related

does count automatically sum up similar values without a group by statement

THIS IS THE INPUT
team_1 team_2 winner
Aus India India
Eng NZ NZ
India SL India
SA Eng Eng
SL Aus Aus
OUTPUT
team_name matches_played no_of_wins
India 2 2
SL 2 NULL
SA 1 NULL
Eng 2 1
Aus 2 1
NZ 1 1
This is the MYSQL solution for the problem:
WITH CTE AS (SELECT team_1 team_name,winner FROM icc_world_cup
UNION ALL
SELECT team_2 team_name,winner FROM icc_world_cup)
SELECT DISTINCT team_name, # first column
COUNT(team_name) as Macthes_played, #second column
(SELECT COUNT(*) FROM
(SELECT IF(team_1=winner,team_1,team_2) win_team FROM icc_world_cup )a
WHERE team_name=win_team GROUP BY win_team) no_of_wins #third column
FROM CTE GROUP BY team_name
The above output is what I got from the code which I have written but the problem is
If I remove the GROUP BY statement in the third column that is
GROUP BY win_team
Then the output was something like this
team_name matches_played no_of_wins
India 2 2
SL 2 0
SA 1 0
Eng 2 1
Aus 2 1
NZ 1 1
How the count is able sum up team india's wins that is 2 without a group by statement, does it have something to with the where clause condition and
NOTICE that the NULL values in the third column were replaced by 0's.
How is it possible that without a group by statement my count function is able to sum up similar values and how the null are changed to 0.
I would use a union approach here:
SELECT team_name, COUNT(*) AS matches_played, SUM(win) AS no_of_wins
FROM
(
SELECT team_1 AS team_name, IF(team_1 = winner, 1, 0) AS win FROM yourTable
UNION ALL
SELECT team_2, IF(team_2 = winner, 1, 0) FROM yourTable
) t
GROUP BY team_name;

LEFT JOIN to return only first row

I am making a join with two tables, tab_usuarios (users) and tab_enderecos (address).
tab_usuarios structure:
id_usuario
nome
usuario
1
Administrador
admin
2
Novo Usuário
teste
3
Joao Silva
jao
tab_enderecos structure:
id_endereco
id_usuario
cidade
uf
2
1
cidade
SP
20
2
Lorena
SP
22
2
Lorena
SP
24
3
Campinas
SP
28
4
Lorena
SP
I have this simple query which brings me the following result:
Select
u.id_usuario,
u.usuario,
u.nome,
e.id_endereco,
e.cidade,
e.uf
From
tab_usuarios u Left Join
tab_enderecos e On u.id_usuario = e.id_usuario
id_usuario
usuario
nome
id_endereco
cidade
uf
1
admin
Administrador
2
cidade
SP
2
user 2
Novo Usuário
22
Lorena
SP
2
user 2
Novo Usuário
20
Lorena
SP
3
jao
Joao Silva
24
Campinas
SP
4
teste
fabio
28
Lorena
SP
What I want is, for example, for id_usuario = 2, I only want to bring the id_endereco = 20, which is the first address that have been inserted on the database.
I tried with min and a couple others.
This should do it, assuming you have MySql 8.0 and not some ancient 5.x version:
SELECT *
FROM (
SELECT u.id_usuario, u.usuario, u.nome, e.id_endereco, e.cidade, e.uf,
row_number() over (partition by u.id_usuario order by e.id_endereco) rn
FROM tab_usuarios u
LEFT JOIN tab_enderecos e On u.id_usuario = e.id_usuario
) t
WHERE rn = 1
See it work here:
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=c506baf8157f82390bb335d074e7614c

results with between and count in a select

How can I make a list where I need the orders to be between the date '2018/10/01' and '2018/10/01', but also only show the customers with more than one order?
I have these orders, but they only work for me separately.
NEW: "The big problem I have is that there are cliente that are repeated in the pedido, but one has the date within the range and the other does not, so you need to filter having a repeated cliente but with at least one pedido within the date 'fecha_ped'."
ejecution:
SELECT cliente.num_cli, nombre, apellidos, fecha_ped
FROM cliente,pedido
where pedido.cliente=cliente.num_cli
group by (pedido.num_ped)
having pedido.fecha_ped between '2018/10/01' and '2018/10/15' ;
this return:
104 Úrsula Delta Camacho 2018-10-05
102 María Sánchez Cid 2018-10-05
106 Pedro Jiménez Ruiz 2018-10-05
105 Carmen Hernández Pío 2018-10-05
107 Raúl Rodrigo Roca 2018-10-05
ejecution2:
SELECT cliente.num_cli, nombre, apellidos, fecha_ped
FROM repartocomidas_tarea5.cliente,pedido
where pedido.cliente=cliente.num_cli
group by (pedido.cliente )
having count(pedido.cliente)>=2 ;
this return:
101 Luis Ramírez Pardo 2018-09-21
102 María Sánchez Cid 2018-09-21
105 Carmen Hernández Pío 2018-09-21
104 Úrsula Delta Camacho 2018-09-22
107 Raúl Rodrigo Roca 2018-09-22
the table pedido contains these columns:
num_ped,fecha_ped, hora_servir,hora_ped,hora_entrega,cliente
the table cliente contains these columns:
num_cli,nombre,apellidos,domicilio,cod_postal,localidad,telefono,tipo_cli,bono_canjebale.
the values of pedido:
100101 2018-09-21 10:00:00 14:30:00 14:35:00 101
100107 2018-09-22 10:55:00 14:30:00 null 101
100102 2018-09-21 10:15:00 14:00:00 13:58:00 102
100110 2018-10-05 10:05:00 14:00:00 14:05:00 102
100103 2018-09-21 10:30:00 15:00:00 null 103
100106 2018-09-22 11:08:00 14:15:00 14:20:00 104
100109 2018-10-05 11:56:00 14:45:00 14:48:00 104
100104 2018-09-21 10:44:00 14:30:00 14:40:00 105
100112 2018-10-05 12:25:00 14:15:00 14:20:00 105
100111 2018-10-05 11:34:00 14:30:00 14:38:00 106
100108 2018-09-22 11:56:00 13:45:00 14:01:00 107
100113 2018-10-05 09:45:00 13:45:00 13:56:00 107
100105 2018-09-21 10:56:00 14:00:00 14:08:00 108
values of cliente:
101 Luis Ramírez Pardo
102 María Sánchez Cid
103 Martín Guerrero López
104 Úrsula Delta Camacho
105 Carmen Hernández Pío
106 Pedro Jiménez Ruiz
107 Raúl Rodrigo Roca
108 Soledad Campillo Molina
109 Lucía Gómez Prados
To combine results use UNION ALL. But make sure they have the same column to select
SELECT cliente.num_cli, nombre, apellidos, fecha_ped
FROM cliente,pedido
where pedido.cliente=cliente.num_cli
group by (pedido.num_ped)
having pedido.fecha_ped between '2018/10/01' and '2018/10/15'
UNION ALL
SELECT cliente.num_cli, nombre, apellidos, fecha_ped
FROM repartocomidas_tarea5.cliente,pedido
where pedido.cliente=cliente.num_cli
group by (pedido.cliente )
having count(pedido.cliente)>=2 ;
But if you want for the result to meet both condition try this.
SELECT cliente.num_cli, nombre, apellidos, fecha_ped
FROM repartocomidas_tarea5.cliente,pedido
where pedido.cliente=cliente.num_cli
group by (pedido.cliente,pedido.num_ped )
having count(pedido.cliente)>=2 AND (pedido.fecha_ped between '2018/10/01' and '2018/10/15');
but if you want just either of the condition change your having to OR like this
having count(pedido.cliente)>=2 OR (pedido.fecha_ped between '2018/10/01' and '2018/10/15');
May I add another suggestion.
SELECT cliente.num_cli, nombre, apellidos, min(fecha_ped),max(fecha_ped),count(*)
FROM cliente JOIN pedido
ON cliente.num_cli=pedido.cliente
WHERE pedido.fecha_ped between '2018-10-01' and '2018-10-15'
GROUP BY pedido.cliente
having COUNT(*) >=2 ;
Edit:
After looking at your data (and creating a fake test data on my side), I've come up with this query below:
SELECT *,COUNT(*) AS "Total Orders"
FROM cliente a LEFT JOIN pedido b ON a.num_cli=b.cliente
WHERE b.fecha_ped BETWEEN '2018-10-01' AND '2018-10-15' GROUP BY num_cli;
The COUNT(*) AS "Total Orders" will show how many orders GROUP BY num_cli between the date you requested. This will show all results.
By adding HAVING COUNT(*) >=2; like query below, will only show cliente that have ordered more than once:
SELECT *,COUNT(*) AS "Total Orders"
FROM cliente a LEFT JOIN pedido b ON a.num_cli=b.cliente
WHERE b.fecha_ped BETWEEN '2018-10-01' AND '2018-10-15' GROUP BY num_cli
HAVING COUNT(*) >=2;
*I edited the b.hora_servir to b.fecha_ped
Edit 2:
Ok, I'm just trying something here. Tell me if this is at least close to what you try to achieve.
SELECT * FROM
(SELECT num_cli,nombre,apellidos,TotalOrders,IF(mdate BETWEEN '2018-10-01' AND '2018-10-15',CONCAT("Last Purchase date on ",mdate),"None") AS "Checking" FROM
(SELECT num_cli,nombre,apellidos,MAX(fecha_ped) mDate,COUNT(*) AS "TotalOrders"
FROM cliente a LEFT JOIN pedido b ON a.num_cli=b.cliente
WHERE b.fecha_ped BETWEEN '2018-10-01' AND '2018-10-15' GROUP BY num_cli
UNION
SELECT num_cli,nombre,apellidos,MAX(fecha_ped) mDate,COUNT(*) AS "TotalOrders"
FROM cliente a LEFT JOIN pedido b ON a.num_cli=b.cliente
WHERE b.fecha_ped NOT BETWEEN '2018-10-01' AND '2018-10-15' GROUP BY num_cli
ORDER BY num_cli) a) b WHERE (TotalOrders=2 OR checking <> "None");
You can do like this :
SELECT DISTINCT(p.cliente),c.num_cli, nombre, apellidos, fecha_ped
FROM pedido AS p
LEFT JOIN cliente AS c ON p.cliente=c.num_cli
WHERE p.fecha_ped >= DATE_FORMAT('YYYY/MM/DD','2018/10/01') AND p.fecha_ped <= DATE_FORMAT('YYYY/MM/DD','2018/10/15') HAVING COUNT(p.cliente)>=2;
In the end I found the solution using a condition to the result of the union of the tables.
SELECT NUMERO , nombre, apellidos FROM
(
(SELECT cliente.num_cli AS NUMERO, CLIENTE.num_cli AS NC, nombre, apellidos, fecha_ped AS FECHA, num_ped, total_pedido as TOTAL
FROM cliente,pedido
where pedido.cliente=cliente.num_cli
group by (pedido.num_ped)
having pedido.fecha_ped between '2018/10/01' and '2018/10/15')
UNION
(SELECT cliente.num_cli AS NUMERO,CLIENTE.num_cli AS NC, nombre, apellidos, fecha_ped AS FECHA, num_ped, COUNT(pedido.cliente) AS TOTAL
FROM repartocomidas_tarea5.cliente,pedido
where pedido.cliente=cliente.num_cli
group by (pedido.cliente )
having (count(pedido.cliente)>=2) )
)
AS RESULTADO
WHERE NUMERO=NC
GROUP BY numero
having (count(nombre)>=2) order by NUMERO ;
;
RESULT:
NUMERO nombre apellidos
102 María Sánchez Cid
104 Úrsula Delta Camacho
105 Carmen Hernández Pío
107 Raúl Rodrigo Roca
Your problem is that you are creating a new column with the numbers of pedidos of each client, so you can avoid this column to fix your problem.
Think that you are trying to insert the query values into a table that dont have the same numbers of columns.
Here you code fixed.
insert into cliente_vip (num_cli, nombre, apellidos)
select p.cliente, c.nombre, c.apellidos
from pedido p
inner join cliente c
on c.num_cli = p.cliente
where p.fecha_ped between '2018-10-01' and '2018-10-15'
group by p.cliente having count(p.cliente) >= 2;

MySQL: Select on GROUP BY only one row with certain criteria

I am having a table with documents where each document has a doc_id but on the same date for the same case_id I might be having two different language versions
doc_id case_id date lang
001-89259 1012/02 2008-11-04 FRA
001-144945 10122/04 2014-06-19 ENG
001-57558 10126/82 1988-06-21 ENG
001-62116 10126/82 1988-06-21 FRA
001-91708 10129/04 2009-03-10 FRA
001-116955 10131/11 2013-03-07 FRA
001-102676 10143/07 2011-01-11 FRA
001-104520 10145/07 2011-04-12 FRA
001-72756 10162/02 2006-03-09 FRA
001-72757 10162/02 2006-03-09 ENG
001-82198 10163/02 2007-09-06 ENG
001-57555 10208/82 1988-05-26 ENG
001-62113 10208/82 1988-05-26 FRA
What I want to do is to select the english version, if available, per case_id, date, otherwise keep the french. My output would then look like:
doc_id case_id date lang
001-89259 1012/02 2008-11-04 FRA
001-144945 10122/04 2014-06-19 ENG
001-57558 10126/82 1988-06-21 ENG -- keep only the english version
001-91708 10129/04 2009-03-10 FRA
001-116955 10131/11 2013-03-07 FRA
001-102676 10143/07 2011-01-11 FRA
001-104520 10145/07 2011-04-12 FRA
001-72757 10162/02 2006-03-09 ENG -- keep only the english version
001-82198 10163/02 2007-09-06 ENG
001-57555 10208/82 1988-05-26 ENG -- keep only the english version
How can I do it with MySQL?
UPDATE:
All answers give the correct result but I marked Görkem's as correct as IMO is the most elegant and straight-forward as of why it works.
I initially accepted Görkem's answer but for some reason it returned one wrong result that Strawberry pointed out. That leaves Strawberry's answer as the most elegant and correct
SELECT DISTINCT COALESCE(e.doc_id,f.doc_id) doc_id
, f.case_id
, f.date
, COALESCE(e.lang,f.lang) lang
FROM my_table f
LEFT
JOIN my_table e
ON e.case_id = f.case_id
AND e.date = f.date
AND e.lang = 'ENG';
SELECT
sorted.doc_id,
sorted.case_id,
sorted.date,
sorted.lang
FROM (
SELECT
doc_id,
case_id,
date,
lang
FROM tbl
ORDER BY FIELD(lang, 'ENG', 'FRA')
) sorted
GROUP BY sorted.case_id
If this SQL is required for some research, there is a way to get the expected result set:
Select SUBSTRING_INDEX(GROUP_CONCAT(doc_id ORDER BY lang ), ',', 1) doc_id, case_id, date, SUBSTRING_INDEX(GROUP_CONCAT(lang ORDER BY lang), ',', 1) lang from table group by case_id,date
SELECT
doc_id,
case_id,
date,
lang,
max(case lang when 'ENG' then 1 else 0 end)
FROM tbl
GROUP BY case_id

mySql query giving result in wrong order

I am using this mysql query to fetch data from DB
SELECT DISTINCT CONCAT( streetObj.street_type, ' ',streetObj.street_name, ', ', neighborhoodObj.name , ', ', cityObj.name, ', ', stateObj.abbreviation ) namet
FROM street streetObj
LEFT
JOIN cep cepObj1
ON cepObj1.street_id = streetObj.street_id
LEFT
JOIN neighborhood neighborhoodObj
ON neighborhoodObj.neighborhood_id = cepObj1.start_neighborhood_id
LEFT
JOIN city cityObj
ON streetObj.city_id = cityObj.city_id
LEFT
JOIN state stateObj
ON stateObj.state_id = cityObj.state_id
WHERE CONCAT(streetObj.street_type,streetObj.street_name) LIKE '%rua%'
AND CONCAT(streetObj.street_type,streetObj.street_name) LIKE '%Gomes%'
AND CONCAT(streetObj.street_type,streetObj.street_name) LIKE '%de%'
AND CONCAT(streetObj.street_type,streetObj.street_name) like '%Ca%'
AND cityObj.city_id = '9668'
ORDER
BY namet ASC
LIMIT 10;
This query is executed when I type
rua Gomes de Ca
And this query result is this
Rua Baltazar Gomes de Alarcão, Jardim Miriam, São ...
Rua Cabo José Gomes de Barros, Conjunto Habitacion...
Rua Cabo Luís Gomes de Quevedo, Parque Novo Mundo,...
Rua Gomes de Carvalho, Vila Olímpia, São Paulo, SP
Rua João Gomes de Mendonça, Jaraguá, São Paulo, SP
Rua João Gomes de Mendonça, Jardim Taipas, São Pau...
Rua Pedro Gomes de Camargo, Vila Rio Branco, São P...
So as you can see i want those results on top which find exact match, But its not working.
In this query i want
Rua Gomes de Carvalho, Vila Olímpia, São Paulo, SP
on top position.
You need to rank the results by the strength of the match, and sort by that. You will have to define the sort yourself. For example:
select ..
from...
ORDER BY
case
when text like "%all my search phrase%" then 1
when text like "%all my%" then 2
when text like "%search phrase%" then 2
when text like "%phrase%" then 3
else 1000 end
DESCENDING
or
ORDER BY
case when text like "%word%" then 1 else 0 end
+
case when text like "%second_word%" then 1 else 0 end
+
.....
DESC
Specifically for your example
select namet from
(select 'Rua Baltazar Gomes de Alarcão, Jardim Miriam, São ...' as namet
union all select 'Rua Cabo José Gomes de Barros, Conjunto Habitacion...'
union all select 'Rua Cabo Luís Gomes de Quevedo, Parque Novo Mundo,...'
union all select 'Rua Gomes de Carvalho, Vila Olímpia, São Paulo, SP'
union all select 'Rua João Gomes de Mendonça, Jaraguá, São Paulo, SP'
union all select 'Rua João Gomes de Mendonça, Jardim Taipas, São Pau...'
union all select 'Rua Pedro Gomes de Camargo, Vila Rio Branco, São P...')tbl
order by
case when namet like "%rua gomes de ca%" then 100 else 0 end+ #high score for full match
case when namet like "%rua%" then 1 else 0 end+ #lower score for partial matches
case when namet like "%Gomes%" then 1 else 0 end+
case when namet like "%de%" then 1 else 0 end+
case when namet like "%ca%" then 1 else 0 end desc LIMIT 10
Although you probably want to write something to split your search phrase into words, search for every word, and rank on number of words matched. You could also look into soundex or levenstein distance for ranking similarity. Doing it in sql though is harder than doing it programatically.