SQL get all rows sorted without duplicates - mysql

I have a table that looks something like this:
________________________
|id|value|date|approved|
-----------------------
What I need to be able to do is get each row where approved = 1. That part is obvious. For each occurrence of value, I only want the most recent row (sorted by date).
Meaning that with a table like this:
________________________
|id|value|date|approved|
-----------------------
|1 |Foo | 5 | 1 |
|2 |Bar | 6 | 1 |
|3 |Foo | 8 | 1 |
-----------------------
I only want the rows with id 2 and 3.
I assume I need to use DISTINCT somehow, but I'm not sure how. Could anyone help me out here?

SELECT m.*
FROM (
SELECT DISTINCT value
FROM mytable
) md
JOIN mytable m
ON m.id =
(
SELECT id
FROM mytable mi
WHERE mi.value = md.value
AND mi.approved = 1
ORDER BY
mi.value DESC, mi.date DESC, mi.id DESC
LIMIT 1
)
Create an index on (value, date, id) for this to work fast.

You need:
select id, value, date, approved where (value, date) in (
select value, max(date)
from your_table
group by value
);

Actually using GROUP BY will yeld better results. try something like this:
SELECT id, value, date, approved FROM table WHERE approved = 1 GROUP BY value ORDER BY date;

select id, value, date, approved
from mytable a
where approved = 1
and date =
(select max(b.date)
from mytable b
where b.approved = 1
and b.value = a.value)

select
id,
value,
date
from
( select
value, max( date ) as LastInstance
from
YourTable
where
approved = 1
group by
value ) PreQuery
join YourTable
on PreQuery.value = YourTable.value
and PreQuery.LastInstance = YourTable.LastInstance
and YourTable.approved = 1
order by
date

Try with this:
SELECT * DISTINCT FROM TABLA WHERE APPROVED = 1 ORDER BY DATE DESC
Hope this helps you.

Related

get the total count but exclude certain condition

Hello I had this table:
id | user_id | status
1 | 34 | x
2 | 35 | x
3 | 42 | x
4 | 42 | y
My goal is to count the data with X status except if the user has a another data with Y status, it will exclude in the count. So instead of 3, it will only count 2 since the 3rd row has another data which is the 4th row with y status.
SELECT * FROM logs
AND user_id NOT IN (SELECT user_id FROM logs WHERE status = 'y')
GROUP BY user_id;
We can try the following aggregation approach:
SELECT COUNT(*) AS cnt
FROM
(
SELECT user_id
FROM logs
GROUP BY user_id
HAVING MIN(status) = MAX(status) AND
MIN(status) = 'x'
) t;
The above logic only counts a user having one or more records only having x status.
You can do it this way, I only modify a bit on your sql
SELECT COUNT(*) FROM (
SELECT u_id FROM tbl WHERE u_id NOT IN
(SELECT u_id FROM tbl WHERE status = 'y')
GROUP BY u_id
) as t
You can use inner join:
SELECT
count(t1.id) AS `cnt`
FROM
`test` AS t1,
`test` AS t2
WHERE
t2.`status`='y'
&& t1.`user_id` != t2.`user_id`;

mysql query lowest option sum

I've got a query returning the following:
ID | Price
---------------
1 | 20
1 | 30
1 | 15
2 | 10
2 | 12
2 | 20
3 | 1
3 | 0
3 | 0
4 | 0
4 | 0
4 | 7
I'm wondering if there's a way I can get the sum of the lowest value for each ID. So in this case it would return 25.
15+10+0+0
You can use a subquery selecting the min price for each id, then sum those values:
select sum(minprice) as overallprice
from (
select min(price) minprice
from yourtable
group by id) t
You can create a sub-query that finds the lowest price per id and take the results from that and sum them together. In pseudo-code:
select
sum(lowest_price)
from (select id, min(price) as lowest_price from prices group by id) lowest_prices
You can do a query like below
Select sum (a) from
(
Select min (price) as a from yourtable
Group by id
) t
Another approach using partition without using group by statement
select sum(price.min_price) from
(select distinct id,min(price) over(partition by id) as min_price from prices) price
Some other approaches would be to use MySQL user variables or a self left join..
MySQL user variable solution
Query
SELECT
SUM(prices.Price)
FROM (
SELECT
prices.Price
, CASE
WHEN #id != prices.id
THEN 1
ELSE 0
END AS isMinGroupValue
, (#id := prices.id)
FROM
prices
CROSS JOIN (
SELECT
#id := 0
) AS init_user_params
ORDER BY
prices.ID ASC
, prices.price ASC
) AS prices
WHERE
prices.isMinGroupValue = 1
see demo https://www.db-fiddle.com/f/nzWqMQAxd7mvq589R7WuZ8/0
Self left join solution
Query
SELECT
SUM(prices1.Price)
FROM
prices prices1
LEFT JOIN
prices prices2
ON
prices1.ID = prices2.ID
AND
prices1.price > prices2.price
WHERE
prices2.ID IS NULL
see demo https://www.db-fiddle.com/f/nzWqMQAxd7mvq589R7WuZ8/1
I would use correlation subquery :
select sum(t.price) as overallprice
from table t
where price = (select min(price) from table t1 where t1.id = t.id);

Unique combination of two columns in mysql or postgres

I have to get unique combinations of two columns.
Eg if the values are :
sender_id recipient_id created_at
1 2 10/11/2014
2 1 10/12/2014
1 2 10/13/2014
1 3 10/14/2014
I want the output to be :
sender_id recipient_id created_at
1 3 10/14/2014
1 2 10/13/2014
I wrote this query :
SELECT DISTINCT ON (sender_id, recipient_id) *
FROM "messages"
WHERE ((recipient_id = 1 and recipient_delete = false)
or (sender_id = 1 and sender_delete = false))
ORDER BY sender_id, recipient_id, created_at DESC
But it outputs this:
sender_id recipient_id created_at
1 3 10/14/2014
2 1 10/12/2014
1 2 10/13/2014
One option to get all pairs, regardless of whether they are forward or backward (for example (1, 2) == (2, 1)) is to select the LEAST() and GREATEST() from each row, and then select distinct values. Using this query:
SELECT DISTINCT LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id)
FROM myTable;
You will get the following output:
| 1 | 2 |
| 1 | 3 |
Once you have that, you can GROUP by these to get the maximum date for each pair:
SELECT LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id), MAX(created_at)
FROM myTable
GROUP BY LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id);
This query will give you the data you need for each pair, but it won't return the actual row from your original table. If there is a row of format | 2 | 1 | 2014-10-15 | this query will return | 1 | 2 | 2014-10-15.
To get the original row from your table, you need to JOIN on the condition that all of the necessary columns match:
SELECT m.*
FROM myTable m
JOIN(
SELECT LEAST(sender_id, recipient_id) AS least,
GREATEST(sender_id, recipient_id) AS greatest,
MAX(created_at) AS maxDate
FROM myTable
GROUP BY LEAST(sender_id, recipient_id), GREATEST(sender_id, recipient_id)) tmp
ON tmp.least = LEAST(m.sender_id, m.recipient_id) AND tmp.greatest = GREATEST(m.sender_id, m.recipient_id) AND tmp.maxDate = m.created_at;
Here is an SQL Fiddle example that matches your expected results.
The initial idea with DISTINCT ON is good, but:
it works with postgres but not with mysql, DISTINCT ON() being a PostgreSQL non-standard extension.
the ON() has to be applied to an expression where (1,2) and (2,1) are equivalent.
So a close query that should work and be efficient for postgres is:
SELECT DISTINCT ON (pair) *,
CASE WHEN sender_id<recipient_id
THEN (sender_id,recipient_id)
ELSE (recipient_id,sender_id)
END AS pair
FROM messages
ORDER BY pair, created_at DESC ;

Order by two columns

Imagine the following MySQL table of orders:
id | name
1 | Mike
2 | Steve
3 | Janet
4 | Juliet
5 | Mike
6 | Jane
This is my current query:
SELECT * FROM table ORDER BY id DESC
However, I'd like to "group" those by name, so that I have orders from the same person listed after one another, however, I cannot do ORDER BY name.
This is my desired output:
id | name
6 | Jane
5 | Mike
1 | Mike
4 | Juliet
3 | Janet
2 | Steve
What's the query for this output?
E.g.:
SELECT y.id
, y.name
FROM my_table x
JOIN my_table y
ON y.name = x.name
GROUP
BY name
, id
ORDER
BY MAX(x.id) DESC
, id DESC;
You need to have special calculation to get their row position.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT Name,
#ord := #ord + 1 ord
FROM
(
SELECT MAX(ID) ID, NAME
FROM TableName
GROUP BY Name
) a, (SELECT #ord := 0) b
ORDER BY ID DESC
) b ON a.Name = b.Name
ORDER BY b.ord, a.ID DESC
SQLFiddle Demo
You can do it via double ORDER BY:
SELECT * FROM t ORDER BY name ASC, id DESC
SELECT * FROM table1
ORDER BY field(NAME,'Mike','Jane') desc,
`ID` desc;
exactly as you asked
You could also try this query if you want to have something which is more generic SQL.
SELECT id, name
FROM ( SELECT id, name, (SELECT MAX(id) from Table1 where name=t.name) AS max_id
FROM Table1 AS t
ORDER BY max_id DESC, id DESC) as x
What about to use group by?
You can group it by name and then order...

MySql select next lower number without using limit

Is it possible to select the next lower number from a table without using limit.
Eg: If my table had 10, 3, 2 , 1 I'm trying to select * from table where col > 10.
The result I'm expecting is 3. I know I can use limit 1, but can it be done without that?
Try
SELECT MAX(no) no
FROM table1
WHERE no < 10
Output:
| NO |
------
| 3 |
SQLFiddle
Try this query
SELECT
*
FROM
(SELECT
#rid:=#rid+1 as rId,
a.*
FROM
tbl a
JOIN
(SELECT #rid:=0) b
ORDER BY
id DESC)tmp
WHERE rId=2;
SQL FIDDLE:
| RID | ID | TYPE | DETAILS |
------------------------------------
| 2 | 28 | Twitter | #sqlfiddle5 |
Another approach
select a.* from supportContacts a inner join
(select max(id) as id
from supportContacts
where
id in (select id from supportContacts where id not in
(select max(id) from supportContacts)))b
on a.id=b.id
SQL FIDDLE:
| ID | TYPE | DETAILS |
------------------------------
| 28 | Twitter | #sqlfiddle5 |
Alternatively, this query will always get the second highest number based on the inner where clause.
SELECT *
FROM
(
SELECT t.col,
(
SELECT COUNT(distinct t2.col)
FROM tableName t2
WHERE t2.col >= t.col
) as rank
FROM tablename t
WHERE col <= 10
) xx
WHERE rank = 2 -- <<== means second highest
SQLFiddle Demo
SQLFiddle Demo (supports duplicate values)
If you want to get next lower number from table
you can get it with this query:
SELECT distinct col FROM table1 a
WHERE 2 = (SELECT count(DISTINCT(b.col)) FROM table1 b WHERE a.col >= b.col);
later again if you want to get third lower number you can just pass 3 in place of 2 in where clause
again if you want to get second higher number, just change the condition of where clause in inner query with
a.col <= b.col