How to use SET with SELECT DISTINCT - mysql

I have a table:
CREATE TABLE `test` (
`id` int(11) NOT NULL,
`pn` varchar(40) NOT NULL,
`price` int(1) NOT NULL,
`company` varchar(30) NOT NULL,
`flag` varchar(1) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
What I want to do is to find cheapest goods (pn) and I've managed to do it:
SELECT DISTINCT min(price), pn, company FROM `test` GROUP BY pn
But how I can also mark cheapest goods with flag. I want for all the results Update tabel, set flag=1.
How to do that? Is it possible to use UPDATE table with SELECT DISTINCT?
Here is a SQL Fiddle:
http://sqlfiddle.com/#!9/ea1b3f/9

First, select distinct with group by is almost never the right thing to do. Presumably, you intend something like:
SELECT min(price), pn, min(company)
FROM `test`
GROUP BY pn;
If you want to set a flag for all the min prices, use a join:
update test t join
(select pn, min(price) as minprice
from test
group by pn
) tt
on t.pn = tt.pn and t.price = tt.minprice
set t.flag = 1;

Related

SQL delete query from Table

Hello everyone I want to remove any redundancy of lines in the circulaire table with the same code product(code_prd) by year (annee), object(objet), sector(secteur), circular number ( num_circulaire )
except product code equal to "-"
sql code of the table
CREATE TABLE `circulaire` (
`id` int(11) NOT NULL,
`code_prd` varchar(255) DEFAULT NULL,
`num_circulaire` double DEFAULT NULL,
`annee` double DEFAULT NULL,
`date` datetime DEFAULT NULL,
`objet` double DEFAULT NULL,
`libelle_prd` varchar(100) DEFAULT NULL,
`secteur` varchar(1) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
This looks like MySQL. You can use delete with join:
delete c
from circulaire c left join
(select code_prd, annee, objet, secteur, num_circulaire, min(id) as min_id
from circulaire
group by code_prd, annee, objet, secteur, num_circulaire
) cc
on c.id = cc.min_id
where c.code_prd <> '-' and
cc.min_id is null;
This calculates the minimum id for the combination of columns you have specified. The LEFT JOIN finds matches on the minimum id for those columns. Only non-matches are deleted.
Try this:
DELETE
FROM
circulaire
WHERE
id IN(
SELECT
*
FROM
(
SELECT
MIN(id)
FROM
circulaire
GROUP BY
code_prd, annee, objet, secteur, num_circulaire
HAVING
COUNT(*) > 1
) temp
)

SQL query to find the greater duplicate row

I've a table in a DB that looks like this:
TABLE `partecipanti` (
`ID` int(11) NOT NULL,
`Name` varchar(30) DEFAULT NULL,
`Surname` varchar(30) DEFAULT NULL,
`Score2` int(11) DEFAULT NULL,
`Zero1` int(11) DEFAULT '0',
`Zero2` int(11) DEFAULT '0',
`Score1` int(10) NOT NULL DEFAULT '0'
)
And a query that looks like this:
SELECT *,Score1 + Score2 as Total FROM partecipanti
ORDER BY Total DESC,Score2 DESC,Zero2 DESC,Score1 DESC, Zero1 DESC;
Now the thing I'd like to do is : when I've a duplicate record (same name and same surname while the other data including ID are differents) pick the row that has the higher score which is stored in the field Total
I was thinking about a nested query,in the first query I order the data and I group them with GROUP BY, then I select the higher element. Could anyone help me please? Thanks.
EDIT:
If I add the DISTINCT statement the query seems to work , is it ok? Thanks.
You can try to run the following query and find the duplicates with max(id).
SELECT Score1, Score2, COUNT(*), Max(ID) AS dupes
FROM participanti
GROUP BY Total
HAVING (COUNT(*) > 1)
Given score2 can be null, try this:
select
p1.*
from
partecipanti p1
join ( select name, surname, max(score1 + coalesce(score2, 0) ) totalScore from partecipanti group by name, surname) p2 on
p1.Name = p2.Name and
p1.Surname = p2.Surname and
p1.score1 + coalesce(p1.score2, 0) = p2.totalScore

MysqL big table query optimization

I have a chatting application. I have an api which returns list of users who the user talked. But it takes a long to mysql return a list messages when it reachs 100000 rows of data.
This is my messages table
CREATE TABLE IF NOT EXISTS `messages` (
`_id` int(11) NOT NULL AUTO_INCREMENT,
`fromid` int(11) NOT NULL,
`toid` int(11) NOT NULL,
`message` text NOT NULL,
`attachments` text NOT NULL,
`status` tinyint(1) NOT NULL DEFAULT '0',
`date` datetime NOT NULL,
`delete` varchar(50) NOT NULL,
`uuid_read` varchar(250) NOT NULL,
PRIMARY KEY (`_id`),
KEY `fromid` (`fromid`,`toid`,`status`,`delete`,`uuid_read`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=118561 ;
and this is my users table (simplified)
CREATE TABLE IF NOT EXISTS `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`login` varchar(50) DEFAULT '',
`sex` tinyint(1) DEFAULT '0',
`status` varchar(255) DEFAULT '',
`avatar` varchar(30) DEFAULT '0',
`last_active` datetime DEFAULT NULL,
`active` tinyint(1) DEFAULT '1',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=15523 ;
And here is my query (for user with id 1930)
select SQL_CALC_FOUND_ROWS `u_id`, `id`, `login`, `sex`, `birthdate`, `avatar`, `online_status`, SUM(`count`) as `count`, SUM(`nr_count`) as `nr_count`, `date`, `last_mesg` from
(
(select `m`.`fromid` as `u_id`, `u`.`id`, `u`.`login`, `u`.`sex`, `u`.`birthdate`, `u`.`avatar`, `u`.`last_active` as online_status, COUNT(`m`.`_id`) as `count`, (COUNT(`m`.`_id`)-SUM(`m`.`status`)) as `nr_count`, `tm`.`date` as `date`, `tm`.`message` as `last_mesg` from `messages` as m inner join `messages` as tm on `tm`.`_id`=(select MAX(`_id`) from `messages` as `tmz` where `tmz`.`fromid`=`m`.`fromid`) left join `users` as u on `u`.`id`=`m`.`fromid` where `m`.`toid`=1930 and `m`.`delete` not like '%1930;%' group by `u`.`id`)
UNION
(select `m`.toid as `u_id`, `u`.`id`, `u`.`login`, `u`.`sex`, `u`.`birthdate`, `u`.`avatar`, `u`.`last_active` as online_status, COUNT(`m`.`_id`) as `count`, 0 as `nr_count`, `tm`.`date` as `date`, `tm`.`message` as `last_mesg` from `messages` as m inner join `messages` as tm on `tm`.`_id`=(select MAX(`_id`) from `messages` as `tmz` where `tmz`.`toid`=`m`.`toid`) left join `users` as u on `u`.`id`=`m`.`toid` where `m`.`fromid`=1930 and `m`.`delete` not like '%1930;%' group by `u`.`id`)
order by `date` desc ) as `f` group by `u_id` order by `date` desc limit 0,10
Please help to optimize this query
What I need,
Who user talked to (name, sex, and etc)
What was the last message (from me or to me)
Count of messages (all)
Count of unread messages (only to me)
The query works well, but takes too long.
The output must be like this
You have some design problems on your query and database.
You should avoid keywords as column names, as that delete column or the count column;
You should avoid selecting columns not declared in the group by without an aggregation function... although MySQL allows this, it's not a standard and you don't have any control on what data will be selected;
Your not like construction may cause a bad behavior on your query because '%1930;%' may match 11930; and 11930 is not equal to 1930;
You should avoid like constructions starting and ending with % wildcard, which will cause the text processing to take longer;
You should design a better way to represent a message deletion, probably a better flag and/or another table to save any important data related with the action;
Try to limit your result before the join conditions (with a derived table) to perform less processing;
I tried to rewrite your query the best way I understood it. I've executed my query in a messages table with ~200.000 rows and no indexes and it performed in 0,15 seconds. But, for sure you should create the right indexes to help it perform better when the amount of data increase.
SELECT SQL_CALC_FOUND_ROWS
u.id,
u.login,
u.sex,
u.birthdate,
u.avatar,
u.last_active AS online_status,
g._count,
CASE WHEN m.toid = 1930
THEN g.nr_count
ELSE 0
END AS nr_count,
m.`date`,
m.message AS last_mesg
FROM
(
SELECT
MAX(_id) AS _id,
COUNT(*) AS _count,
COUNT(*) - SUM(m.status) AS nr_count
FROM messages m
WHERE 1=1
AND m.`delete` NOT LIKE '%1930;%'
AND
(0=1
OR m.fromid = 1930
OR m.toid = 1930
)
GROUP BY
CASE WHEN m.fromid = 1930
THEN m.toid
ELSE m.fromid
END
ORDER BY MAX(`date`) DESC
LIMIT 0, 10
) g
INNER JOIN messages AS m ON 1=1
AND m._id = g._id
LEFT JOIN users AS u ON 0=1
OR (m.fromid <> 1930 AND u.id = m.fromid)
OR (m.toid <> 1930 AND u.id = m.toid)
ORDER BY m.`date` DESC
;

sorting with Order By in mysql

I am using mysql as database and i have a table like the one below.
CREATE TABLE IF NOT EXISTS `logins` (
`id` int(255) NOT NULL AUTO_INCREMENT,
`userid` varchar(255) NOT NULL,
`date` varchar(255) NOT NULL,
`status` varchar(255) NOT NULL,
KEY `id` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=346 ;
I want to sort mysql results with order by.The problem is when i use this sql it takes only the first recod of date. Which is an older date. I want the newest date. last login date of user.
SELECT * FROM `logins` WHERE `status`='valid' GROUP BY `userid` ORDER BY `date` DESC
Any suggestions?
To do this you use a sub query to get the latest record for each user id and then join that to the logins table to get the rest of the details
SELECT logins.*
FROM logins
INNER JOIN
(
SELECT userid, MAX(`date`) AS max_date
FROM `logins`
WHERE `status` = 'valid'
GROUP BY `userid`
) sub0
ON logins.userid = sub0.userid
AND logins.`date` = sub0.max_date
WHERE `status` = 'valid'
You almost had it. Assuming id and userId doesn't evolve from one login to another, asking the MAX date should give you the expected result.
SELECT id, userId, MAX(`date`) AS lastDate, 'valid'
FROM `logins`
WHERE `status`='valid'
GROUP BY `userid`
ORDER BY `lastDate` DESC
Please note that you would need a JOIN if there were data that change between logins in the table.

complicated sql query returns a result with empty tables

I have three empty tables
--
-- Tabellenstruktur für Tabelle `projects`
--
CREATE TABLE IF NOT EXISTS `projects` (
`id_project` int(11) NOT NULL AUTO_INCREMENT,
`id_plan` int(11) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`description` longtext NOT NULL,
PRIMARY KEY (`id_project`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `project_plans`
--
CREATE TABLE IF NOT EXISTS `project_plans` (
`id_plan` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`description` longtext NOT NULL,
`max_projects` int(11) DEFAULT NULL,
`max_member` int(11) DEFAULT NULL,
`max_filestorage` bigint(20) NOT NULL DEFAULT '3221225472' COMMENT '3GB Speicherplatz',
PRIMARY KEY (`id_plan`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `project_users`
--
CREATE TABLE IF NOT EXISTS `project_users` (
`id_user` int(11) NOT NULL,
`id_project` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
All these tables are empty but i get a result with my query?
my query:
SELECT
A.id_plan,
A.name AS plan_name,
A.description AS plan_description,
A.max_projects,
A.max_member,
A.max_filestorage,
B.id_plan,
B.name AS project_name,
B.description AS project_description,
C.id_user,
C.id_project,
COUNT(*) AS max_project_member
FROM
".$this->config_vars["projects_plans_table"]." AS A
LEFT JOIN
".$this->config_vars["projects_table"]." AS B
ON
B.id_plan = A.id_plan
LEFT JOIN
".$this->config_vars["projects_user_table"]." AS C
ON
C.id_project = B.id_project
WHERE
C.id_project = '".$id."'
&& B.deleted = '0'
i think the problem is the COUNT (*) AS ...
how i can solve the problem?
For one, you are getting a record explicitly due to the COUNT(). Even though you have no records, you are asking the engine how many records which at worst case will return zero. Count(), like other aggregates are anticipated to have a group by, so even though you don't have one, you are still asking.
So the engine is basically stating hey... there are no records, but I have to send you a record so you can get the count() column to look at and do with what you will. So, it is doing what you asked.
Now, for the comment to the other question where you asked...
Yes but i want to count the project member from a project, how i can count the users from project_users where all users have the id_project 1.
Since you only care about a count, and not the specific WHO involved, you can get this result directly from the project_users table (which should have an index on both the ID_User and another on the ID_Project. Then
select count(*)
from project_users
where id_project = 1
To expand from basis of your original question to get the extra details, I would do...
select
p.id_project,
p.id_plan,
p.name as projectName,
p.description as projectDescription,
pp.name as planName,
pp.description as planDescription,
pp.max_projects,
pp.max_member,
pp.max_filestorage,
PJCnt.ProjectMemberCount
from
( select id_project,
count(*) as ProjectMemberCount
from
project_users
where
id_project = 1 ) PJCnt
JOIN Projects p
on PJCnt.id_project = p.id_project
JOIN Project_Plans PP
on p.id_plan = pp.id_plan
Now, based on this layout of tables, a plan can have a max member count, but there is nothing indicating max members for the plan based on all projects, or max per SINGLE project. So, if a plan allows for 20 people, can there be 20 people for 10 different projects under the same plan? That's something only you would know the impact of... just something to consider what you are asking for.
Your cleaned-up query should look like :
See sqlfidle demo as well : http://sqlfiddle.com/#!2/e693f5/9
SELECT
A.id_plan,
A.name AS plan_name,
A.description AS plan_description,
A.max_projects,
A.max_member,
A.max_filestorage,
B.id_plan,
B.name AS project_name,
B.description AS project_description,
C.id_user,
C.id_project,
COUNT(*) AS max_project_member
FROM
project_plans AS A
LEFT JOIN
projects AS B
ON
B.id_plan = A.id_plan
LEFT JOIN
project_users AS C
ON
C.id_project = B.id_project
WHERE
C.id_project = '".$id."';
This will return you null values for all the cols from the select because you have one legit return form the result set and that is the count(*) output 0.
To fix this just add a group by at the end (see group by example http://sqlfiddle.com/#!2/14d46/2) or
Remove the count(*) and the null values will be gone as well as the count(*) values 0
See simple sql example here : http://sqlfiddle.com/#!2/ab7dd/5
Just comment the count() and you fixed you null problem!