Select rows with non matching column - mysql

I am trying to retrieve rows with same Volume value or with only 1 Volume, but could not come up with a SQL logic.
Data:
ID
Volume
A
100
A
100
B
101
B
102
B
103
B
104
C
400
Required Output:
ID
Volume
A
100
A
100
C
400

This one is achievable using a subquery.
select * from test where col1 in (
select t.col1
from(
select col1, col2,
dense_rank() over (partition by col1 order by col2) as dr
from test) t
group by t.col1
having sum(case when t.dr = 1 then 0 else t.dr end) = 0)
Try this dbfiddle.

This can be done on a more easy way:
select t1.id,
t1.volume
from tbl t1
inner join (select id
from tbl
group by id
having count(distinct volume) = 1
) as t2 on t1.id=t2.id;
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=92bc234e631a1106b0e322bc4954d696
having count(distinct volume) = 1 will return only the id that have the same volume , including the id with just one volume.

I'd naturally be inclined towards Ergest Basha's pattern.
It can also be expressed using NOT EXISTS()
SELECT
t.*
FROM
tbl AS t
WHERE
NOT EXISTS (
SELECT *
FROM tbl
WHERE id = t.id
AND volume <> t.volume
)
https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=243c42008f527391514d1ad124730587

Related

Retrieve last non-null record of every column for each record_id in MySQL

I have this MySQL table named records. Below is its contents.
id record_id Data1 Data2 Time
1 1 null 1 1/1/16
2 1 1 null 1/3/16
3 1 2 null 1/4/16
4 1 null 3 1/5/16
5 2 1 null 2/1/16
6 2 1 null 2/3/16
7 2 7 null 2/4/16
8 2 null 5 2/5/16
I would like to have a MySQL query to retrieve the last non-null record of each column for each record_id. The result would look something like;
record_id Data1 Data2 Time
1 2 3 1/5/16
2 7 5 2/5/16
The tricky part to this problem is that multiple columns are involved.
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT record_id, MAX(Time) AS Time
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Time = t2.Time
If you simply want the greatest value for the data and time columns, then see the answer given by #Matt. But your language makes it unclear what you really want.
Update:
Something like this might give the results you want:
SELECT a.record_id,
a.Data1,
b.Data2,
c.Time
FROM
(
SELECT t1.record_id,
t1.Data1
FROM yourTable t1
INNER JOIN
(
SELECT record_id,
MAX(CASE WHEN Data1 IS NULL THEN 0 ELSE id END) AS Data1Id
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Id = t2.Data1Id
) a
INNER JOIN
(
SELECT t1.record_id,
t1.Data2
FROM yourTable t1
INNER JOIN
(
SELECT record_id,
MAX(CASE WHEN Data2 IS NULL THEN 0 ELSE id END) AS Data2Id
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Id = t2.Data2Id
) b
ON a.record_id = b.record_id
INNER JOIN
(
SELECT t1.record_id,
t1.Time
FROM yourTable t1
INNER JOIN
(
SELECT record_id,
MAX(CASE WHEN Data2 IS NULL THEN 0 ELSE id END) AS TimeId
FROM yourTable
GROUP BY record_id
) t2
ON t1.record_id = t2.record_id AND
t1.Id = t2.TimeId
) c
ON a.record_id = c.record_id
Demo Here:
SQLFiddle
This one may solve your problem:
select
record_id,
substring_index(group_concat(Data1 order by Time desc), ',', 1) Data1,
substring_index(group_concat(Data2 order by Time desc), ',', 1) Data2,
substring_index(group_concat(Time order by Time desc), ',', 1) Time
from records
group by record_id
;
It may not be as fast as other answers, but is another version... give it a try. If you have a Data3 column in your table, you can copy/paste the Data1 column and just change all references of this column to the new one.
Just to explain how this works: the group_concat function concatenates all non-null values of a column with a separator (, by default). You can order the column before the concatenation. It works a bit like a window function in Oracle, Postgre, and others... The substring_index is just getting the first concatenated value, as the list is in a descending order of time.
it looks like you are just wanting the maximum data1, max data2, and max time which would be simple aggregation:
SELECT
record_id
,MAX(Data1) as Data1
,MAX(Data2) as Data2
,MAX(Time) as Time
FROM
yourTable
GROUP BY
record_id
SQL fiddle for it http://www.sqlfiddle.com/#!9/d95bc1/2
If latest non-null value per column is desired you can use:
SELECT t.record_id, MAX(t.Data1) as Data1, MAX(t.Data2) as Data2, MAX(t.Time) as Time
FROM
yourTable t
LEFT JOIN
(
SELECT
record_id, MAX(Time) as MaxTime
FROM
yourTable t
WHERE
Data1 IS NOT NULL
GROUP BY
record_id
) d1
ON t.record_id = d1.record_id
AND t.Time = d1.MaxTime
LEFT JOIN
(
SELECT
record_id, MAX(Time) as MaxTime
FROM
yourTable t
WHERE
Data2 IS NOT NULL
GROUP BY
record_id
) d2
ON t.record_id = d2.record_id
AND t.Time = d2.MaxTime
WHERE
d1.record_id IS NOT NULL
OR d2.record_id IS NOT NULL
GROUP BY
t.record_id
Using Tim's method you can actually still get to your results looking at the Latest Data1 record and then the latest Data2 record and then aggregating so they are not purely the MAX of everything but rather representative of the latest 2 records 1 for Data1 and 1 for Data2.
SQL fiddle for this part: http://www.sqlfiddle.com/#!9/d95bc1/10
I would like to have a MySQL query to retrieve the last non-null record of each column for each record_id.
Of course, what is still somewhat unclear is how you determine that a row is the last row, since rows in a database are by definition unordered.
So, my interpretation is that you want the last non-null Data1, Data2 and Time column values for each distinct record_id value. And a row value is considered last if it has a higher id value than another row value.
Assuming my understanding is correct, the following query would work:
select t.record_id,
(select t2.Data1
from tbl t2
where t2.record_id = t.record_id
and t2.Data1 is not null
order by t2.id desc
limit 1) as Data1,
(select t2.Data2
from tbl t2
where t2.record_id = t.record_id
and t2.Data2 is not null
order by t2.id desc
limit 1) as Data2,
(select t2.Time
from tbl t2
where t2.record_id = t.record_id
and t2.Time is not null
order by t2.id desc
limit 1) as Time
from tbl t
group by t.record_id
order by t.record_id
SQLFiddle Demo

MySQl Query to select count until you hit a specific value

I have this table e.g.:
Id StatusDate Status
1 20-08-2014
1 15-08-2014
1 09-08-2014 P
2 17-08-2014
1 10-08-2014
2 12-08-2014
2 06-07-2014 P
1 30-07-2014
2 02-07-2014
2 01-07-2014 P
...... and so on
I want to select count by ID where status is blank until I hit the first 'P' in ascending order of date group by ID. So my results will be like this.
ID Count
1 3
2 2
Try it out. Not tested
SELECT t1.ID, count(*) FROM table t1
WHERE t1.StatusDate >= (SELECT MAX(t2.StatusDate) FROM table t2
WHERE t1.ID = t2.ID AND t2.Status = 'P')
GROUP BY t1.ID
Assuming your table name is StatusTable This will work:
SELECT
ID,
COUNT(*) AS `Count`
FROM StatusTable AS st
WHERE
st.Status = ''
AND st.StatusDate > (
SELECT st2.StatusDate
FROM `StatusTable` AS st2
WHERE st.ID = st2.ID
AND st2.Status = 'P'
ORDER BY st2.StatusDate DESC
LIMIT 1
)
GROUP BY st.ID
ORDER BY st.ID
One option is to use a JOIN and COUNT rows which have a lower statusdate value, like this:
SELECT t1.id, SUM(CASE WHEN t1.statusdate > t2.statusdate THEN 1 ELSE 0 END) AS mycount
FROM t t1 JOIN (
SELECT id, MIN(statusdate) statusdate
FROM t
WHERE status = 'P'
GROUP BY id
) t2
ON t1.id = t2.id
GROUP BY t1.id
Working Demo: http://sqlfiddle.com/#!2/d9d91/2

How to show MIN value with whole table

I have table like:
id col1 col2
1 a 55
2 b 77
In result i want to see:
id col1 col2 MIN(col2)
1 a 55 55
2 b 77
Something like that, or in other case, how i can get one minimum value with whole table.
You can use a CROSS JOIN with a subquery which will select the min(col2) value for the entire table:
select t1.id,
t1.col1,
t1.col2,
t2.minCol2
from yourtable t1
cross join
(
select min(col2) minCol2
from yourtable
) t2
See SQL Fiddle with Demo.
If you want to expand this to only show the min(col2) value on the first row, then you could use user-defined variables:
select id,
col1,
col2,
case when rn = 1 then mincol2 else '' end mincol2
from
(
select t1.id,
t1.col1,
t1.col2,
t2.minCol2,
#row:=case when #prev:=t1.id then #row else 0 end +1 rn,
#prev:=t1.id
from yourtable t1
cross join
(
select min(col2) minCol2
from yourtable
) t2
cross join (select #row:=0, #prev:=null) r
order by t1.id
) d
order by id
See SQL Fiddle with Demo
If you had more than one column that you want to compare, then you could unpivot the data using a UNION ALL query and then select the min value for the result:
select t1.id,
t1.col1,
t1.col2,
t2.MinCol
from yourtable t1
cross join
(
select min(col) MinCol
from
(
select col2 col
from yourtable
union all
select col3
from yourtable
) src
) t2
See SQL Fiddle with Demo
You can't. The number of columns is fixed, so you can get the minimum value on all the rows as described by #bluefeet.
You could get it on a smaller number of rows (typically 1) by using the logic:
(case when t2.minCol2 = t1.col2 then t2.minCol2 end)
But this would put NULLs on the other rows.

Combine two Mysql SUM select query to one query

I have below two query's SUM the values
Query1:*
SELECT SUM(price) FROM TABLE1 WHERE acc_id = '555'
Query2:
SELECT SUM(price) FROM TABLE2 WHERE account = '555' && active='1'
I try to combine this two query but give wrong sum result , for example if query1 sum is: -86500 and Query2 sum is: 76000 , RESULT must be -10500 but result shown with a number like -486000
I'm trying like this, but i'm not getting expected result.
SELECT SUM(t1.price + t2.price) AS TotalCredit
FROM TABLE1 AS t1, TABLE2 AS t2
WHERE t1.`acc_id` = '555' && t2.`Account`='555' && t2.`Active`='1'
Table image :
Due to join the number of records get duplicated and you get a higher value for sum
try this
SELECT sum(prc)
FROM (
SELECT SUM(price) prc FROM TABLE1 WHERE acc_id = '555'
union all
SELECT SUM(price) prc FROM TABLE2 WHERE account = '555' && active='1'
) a
Try this
SELECT SUM(C.TOTAL) AS TOTAL_CREDIT FROM (SELECT SUM(A.price) AS TOTAL FROM TABLE1 A WHERE A.acc_id = '555'
UNION ALL
SELECT SUM(B.price) AS TOTAL FROM TABLE2 B WHERE B.account = '555' && B.active='1') C;
try that
SELECT (SUM(t1.price) + SUM(t2.price)) AS TotalCredit
FROM TABLE1 AS t1, TABLE2 AS t2
WHERE t1.`acc_id` = '555' && t2.`Account`='555' && t2.`Active`='1'
try this
SELECT (t1.price + t2.price) AS TotalCredit
FROM TABLE1 AS t1, TABLE2 AS t2
WHERE t1.`acc_id` = '555' && t2.`Account`='555' && t2.`Active`='1'
EDIT:
here what you looking for i think
SELECT (SUM(t1.price)+SUM(t2.price) )/2 AS TotalCredit
FROM Table1 AS t1, Table2 AS t2
WHERE t1.`acc_id` = '555' && t2.`account`='555' && t2.`active`='1'
DEMO FIDDLE HERE
How about this:
SELECT SUM(a)
FROM
(SELECT SUM(price) AS a
FROM TABLE1
WHERE acc_id = '555'
UNION ALL
SELECT SUM(price) AS a
FROM TABLE2
WHERE account = '555' && active='1')
Join could be better. :) Would be even better if you could have showed us the table schema. Here is a solution based on some assumed sample data.
Sample data:
Table1:
ID PRICE
11 200
55 300
33 200
44 100
55 500
Table2:
ID PRICE ACTIVE
1 200 0
2 300 1
55 200 0
55 100 1
55 400 1
SQLFIDDLE DEMO
Query:
select sum(t.price) + x.tb2credit
from tb1 as t
inner join
(SELECT id, SUM(price) AS Tb2Credit
FROM tb2
WHERE id = 55
and `Active`=1) x
on t.id = x.id
Results:
SUM(T.PRICE) + X.TB2CREDIT
1300

MySQL GROUP BY and HAVING

I'm grouping my results based on a column X and I want to return the rows that has highest Column Y's value in the group.
SELECT *
FROM mytable
GROUP BY col1
HAVING col2 >= (SELECT MAX(col2)
FROM mytable AS mytable2
WHERE mytable2.col1 = mytable.col1 GROUP BY mytable2.col1)
I want to optimize the query above. Is it doable without sub-queries?
I found the solution and it's simpler than you think:
SELECT * FROM (SELECT * FROM mytable ORDER BY col2 DESC) temp GROUP BY col1
Runs in 5 milliseconds on 20,000 rows.
Using a derived table/inline view for a JOIN:
SELECT x.*
FROM mytable x
JOIN (SELECT t.col1,
MAX(t.col2) AS max_col2
FROM MYTABLE t
GROUP BY t.col1) y ON y.col1 = x.col1
AND y.max_col2 >= x.col2
Be aware that this will duplicate x records if there's more than one related y record. To remove duplicates, use DISTINCT:
SELECT DISTINCT x.*
FROM mytable x
JOIN (SELECT t.col1,
MAX(t.col2) AS max_col2
FROM MYTABLE t
GROUP BY t.col1) y ON y.col1 = x.col1
AND y.max_col2 >= x.col2
The following is untested, but will not return duplicates (assuming valid):
SELECT x.*
FROM mytable x
WHERE EXISTS (SELECT NULL
FROM MYTABLE y
WHERE y.col1 = x.col1
GROUP BY y.col1
HAVING MAX(y.col2) >= x.col2)
Your Col2 never be > then MAX(col2) so i suggest to use col2 = MAX(col2)
so HERE is the QUERY
SELECT * FROM mytable GROUP BY col1 HAVING col2 = MAX( col2 )