sql order by not working with group by only - mysql

I have one table stock activity where i have multiple records attached with single item_id. note item_id is playing foreign key role here in stock activity table . so actually i am tracking the item(in,out) of inventory. now i want to retrieve the last record activity stored in the table. i have written query which is supposed to be returning the last record from the table but it is returning the first record ..
Columns are :
activity_id pk
item_id fk
balance int(11)
Here is my query:
SELECT DISTINCT(item_id),balance
FROM `stock_activity`
GROUP BY (item_id)
ORDER BY(activity_id) DESC

Remember if a column that doesn't belongs to the grouping key is being referenced without any sort of aggregation so such statement is impossible.
So remember a little formula to came our this problem.
SELECT * FROM
(
SELECT * FROM `table`
ORDER BY AnotherColumn
) t1
GROUP BY SomeColumn
;
Modify your query like this and hope it will work fine!!!.
SELECT * FROM(
SELECT DISTINCT(item_id),balance
FROM `stock_activity`
ORDER BY(activity_id) DESC
) t1
GROUP BY (item_id)

This is a common problem folks have an issue with. you want the GROUPWISE MAXIMUM (or MINIMUM) of a column. Fortunately such an example exists right in the tutorial section of the manual

Related

Assigning one-column subquery to column

So I've got a table with a primary key id and a foreign key target_id that points to an id. What I'm trying to do is set each target_id to a random id, that is not itself.
At the moment, this is how I get the randomized id's:
SELECT id FROM (SELECT id FROM test) AS sub ORDER BY RAND()
However, when I try to assign that subquery of randomized id's to the column target_id, an error is thrown saying that the subquery returns more than one row.
However, when I tried
UPDATE SET `target_id` = `id`
to see if columns can be directly copied, it worked, showing that IS possible. But why can't one column of my subquery be copied into a column of my table?
Sorry if I worded my question really weirdly; I'm not very experienced in MySql :/
Thanks! :D
As you might guess from the link in my comment to your question - I did not have time to explain further at the time ... - I assume that what you actually want is to shuffle the values of column target_id around. In Mysql this seems to be a non-trivial task, as the post in my comment shows.
I also played around a bit further and came up with this "variation of the theme" using user defined variables. I first tried using a temporary table, but this can only be referenced once so I had to revert to using a "conventional" table tmp in the end:
drop table if exists tmp;
select #n:=0;select #m:=0;
create table tmp select * from
(select *,#n:=#n+1 nr
from (select id,#m:=#m+1 n from tbl order by rand()) rnd
order by rand()
) rnd ;
update tbl s
inner join tmp a on a.id=s.id
inner join tmp b on b.nr=a.n
inner join tbl t on t.id=b.id
set t.target_id=s.target_id;
Check out the little demo here: http://rextester.com/XYF73480

Remove Duplicate record from Mysql Table using Group By

I have a table structure and data below.
I need to remove duplicate record from the table list. My confusion is that when I am firing query
SELECT * FROM `table` GROUP BY CONCAT(`name`,department)
then giving me correct list(12 records).
Same query when I am using the subquery:
SELECT *
FROM `table` WHERE id IN (SELECT id FROM `table` GROUP BY CONCAT(`name`,department))
It returning all record which is wrong.
So, My question is why group by in subquery is not woking.
Actually as Tim mentioned in his answer that it to get first unique record by group by clause is not a standard feature of sql but mysql allows it till mysql5.6.16 version but from 5.6.21 it has been changed.
Just change mysql version in your sql fiddle and check that you will get what you want.
In the query
SELECT * FROM `table` GROUP BY CONCAT(`name`,department)
You are selecting the id column, which is a non-aggregate column. Many RDBMS would give you an error, but MySQL allows this for performance reasons. This means MySQL has to choose which record to retain in the result set. Based on the result set in your original problem, it appears that MySQL is retaining the id of the first duplicate record, in cases where a group has more than one member.
In the query
SELECT *
FROM `table`
WHERE id IN
(
SELECT id FROM `table` GROUP BY CONCAT(`name`,department)
)
you are also selecting a non-aggregate column in the subquery. It appears that MySQL actually decides which id value to be retained in the subquery based on the id value in the outer query. That is, for each id value in table, MySQL performs the subquery and then selectively chooses to retain a record in the group if two id values match.
You should avoid using a non-aggregate column in a query with GROUP BY, because it is a violation of the ANSI standard, and as you have seen here it can result in unexpected results. If you give us more information about what result set you want, we can give you a correct query which will avoid this problem.
I welcome anyone who has documentation to support these observations to either edit my question or post a new one.
You can JOIN the grouped ids with that of table ids, so that you can get desired results.
Example:
SELECT t.* FROM so_q32175332 t
JOIN ( SELECT id FROM so_q32175332
GROUP BY CONCAT( name, department ) ) f
ON t.id = f.id
ORDER BY CONCAT( name, department );
Here order by was added just to compare directly the * results on group.
Demo on SQL Fiddle: http://sqlfiddle.com/#!9/d715a/1

Simply delete duplicate content in a sql table

I wanted to know if there is an easy way to remove duplicates from a table sql.
Rather than fetch the whole table and delete the data if they appear twice.
Thank you in advance
This is my structure :
CREATE TABLE IF NOT EXISTS `mups` (
`idgroupe` varchar(15) NOT NULL,
`fan` bigint(20) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
If you are using Sql Server
Check this: SQL SERVER – 2005 – 2008 – Delete Duplicate Rows
Sample Code using CTE:
/* Delete Duplicate records */
WITH CTE (COl1,Col2, DuplicateCount)
AS
(
SELECT COl1,Col2,
ROW_NUMBER() OVER(PARTITION BY COl1,Col2 ORDER BY Col1) AS DuplicateCount
FROM DuplicateRcordTable
)
DELETE
FROM CTE
WHERE DuplicateCount > 1
GO
Add a calculated column that takes the checksum of the entire row. Search for any duplicate checksums, rank and remove the duplicates.
you can do something like this :
DELETE from yourTable WHERE tableID in
(SELECT clone.tableID
from yourTable origine,
yourTable clone
where clone.tableID= origine.tableID)
But in the WHERE, you can either compare the indexes or compare each other fields...
depending on how you find your doubles.
note, this solution has the advantage of letting you choose what IS a double (if the PK changes for example)
You can find the duplicates by joining the table to itself, doing a group by the fields you are looking for duplicates in, and a having clause where count is greater than one.
Let's say your table name is customers, and your looking for duplicate name fields.
select cust_out.name, count(cust_count.name)
from customers cust_out
inner join customers cust_count on cust_out.name = cust_count.name
group by cust_out.name
having count(cust_count.name) > 1
If you use this in a delete statement you would be deleting all the duplicate records, when you probably intend to keep on of the records.
So to select the records to delete,
select cust_dup.id
from customers cust
inner join customers cust_dup on cust.name = cust_dup.name and cust_dup.id > cust.id
group by cust_dup.id

Sorting MySQL query results by the IN clause

i have a mysql like this
SELECT * FROM table WHERE id IN(3245,76,3466,998,12984,4466,931,50,728)
is there any way i could get the results of the query ordered by the order the id's have in the IN clause?
Thanks a lot
UPDATE: i've just found this question covered here
Althought, my IN clause does contain up to 5000 id's, so would the use of 'FIELD' the best solution out there?
If you don't mind repeating the sequence of ids, using ORDER BY FIELD should do the trick:
SELECT * FROM table WHERE id IN(3245,76,3466,998,12984,4466,931,50,728)
ORDER BY FIELD (id,3245,76,3466,998,12984,4466,931,50,728)
As #vyegorov noted in the comments, for a large number of ids, you may have to create a temporary table to hold the actual order
CREATE TEMPORARY TABLE `temp_sort` (
`id` INT NOT NULL ,
`seq` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
PRIMARY KEY ( `seq` ) ,
UNIQUE ( `id` )
);
INSERT INTO temp_sort (id) VALUES (3245),(76),(3466);
SELECT * FROM table
INNER JOIN temp_sort ON (temp_sort.id=table.id)
ORDER BY temp_sort.seq;
Since you have up to 5000 items in your IN clause, you really need to make a separate table that contains a list of your ids that you want to include. Put an order column in that table and then do an inner join and order by the order column. That solves both problems.
Like so:
SELECT table.*
FROM table
INNER JOIN tblIDs ON tblIDs.id = table.id
ORDER BY tblIDs.OrderValue
Your new table (tblIDs) would look like this:
id OrderValue
3245 1
76 2
3466 3
By doing an INNER JOIN on the id columns, you will ensure that only those rows in your original table that have one of your listed id numbers will be displayed. The OrderValue column will allow you to easily and efficiently sort your query and it will also allow you to easily change the sort order without hacking a query apart.
Not unless you make those ids an attribute. And even then, it will order them asc/desc, not out of order like you have them.
try doing this.
SELECT your_column_name * FROM table WHERE id IN(3245,76,3466,998,12984,4466,931,50,728)
ORDER BY your_column_name

MySQL: how to keep top 1000 rows for each value of a column

I need some help with this query: I want to delete the following rows from my table.
The table has 4 columns: UserID, UserName, Tag, Score.
I want to keep only the top 1000 users with highest score, for each Tag.
So I need to order by score, and to keep the first 1000 users for each tag.
Thanks
Based on the other answer,
DELETE FROM table t1
WHERE UserID NOT IN (SELECT userID FROM table t2
WHERE t2.tag = t1.tag
ORDER BY Score DESC LIMIT 1000);
This assumes that UserID is a unique/primary key field. If it's not, you could do something like this:
DELETE FROM table t1
WHERE UserID||' '||Tag NOT IN (SELECT UserID||' '||Tag FROM table t2
WHERE t2.tag = t1.tag
ORDER BY Score DESC LIMIT 1000);
Putting the spaces in because I know I've had to do similar on Oracle, not sure how MySQL would handle it without.
You should be able to fix this with a subquery although I'm not completely sure if MySQL supports that within delete queries. I do know that it can give problems with update queries (I've had some segfaults in MySQL because of this)
Either way... this should work (atleast, it would in Postgres)
DELETE FROM table WHERE id NOT IN (SELECT id FROM table ORDER BY Score DESC LIMIT 1000)
Assuming that id is your primary key ofcourse.
Solution using a temporary table:
Do note that you'll have to repeat this process for every tag. I can't think of a way to do it in 1 query in MySQL 5.0
CREATE TEMPORARY TABLE top_users
SELECT * FROM table
WHERE Tag = ...
GROUP BY UserID
ORDER BY Score DESC
LIMIT 1000;
DELETE FROM table WHERE Tag = ...;
INSERT INTO table
SELECT * FROM top_users;
Maybe a temporary table, who is filled with a select clause
SELECT * FROM table ORDER BY Score DESC LIMIT 1000;
I dont have the syntax handy for creating the temporary table, but that will atleast get your the rows you want.
Be careful with delete commands... Better to create a new view/table.