Mysql - Select at least one or select none - mysql

I have a table as so...
----------------------------------------
| id | name | group | number |
----------------------------------------
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 3 | james | 2 | 2 |
| 4 | steven | 2 | 5 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
----------------------------------------
I'm running a select like so:
SELECT * FROM table WHERE number IN (2,3);
The problem im trying to solve is that I want to only grab get results from groups that have 1 or more rows of each number. For instance the above query is returning id's 1-2-3-5-6, when I'd like the results to exclude id 3 since the group of '2' can only return 1 result for the number of '2' and not for BOTH 2 and 3, since there's no row with the number 3 for the group 2 i'd like it to not even select id 3 at all.
Any help would be great.

Try it this way
SELECT *
FROM table1 t
WHERE number IN(2, 3)
AND EXISTS
(
SELECT *
FROM table1
WHERE number IN(2, 3)
AND `group` = t.`group`
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
)
or
SELECT *
FROM table1 t JOIN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
) q
ON t.`group` = q.`group`;
or
SELECT *
FROM table1
WHERE `group` IN
(
SELECT `group`
FROM table1
WHERE number IN(2, 3)
GROUP BY `group`
HAVING MAX(number = 2) > 0
AND MAX(number = 3) > 0
);
Sample output (for both queries):
| ID | NAME | GROUP | NUMBER |
|----|-------|-------|--------|
| 1 | joey | 1 | 2 |
| 2 | keidy | 1 | 3 |
| 5 | jason | 3 | 2 |
| 6 | shane | 3 | 3 |
Here is SQLFiddle demo

On this, you can approach from a fun way with multiple joins for what you WANT qualified, OR, apply a prequery to get all qualified groups as others have suggested, but readability is a bit off for me..
Anyhow, here's an approach going through the table once, but with joins
select DISTINCT
T.id,
T.Name,
T.Group,
T.Number
from
YourTable T
Join YourTable T2
on T.Group = T2.Group AND T2.Group = 2
Join YourTable T3
on T.Group = T3.Group AND T3.Group = 3
where
T.Number IN ( 2, 3 )
So on the first record, it is pointing to by it's own group to the T2 group AND the T2 group is specifically a 2... Then again, but testing the group for the T3 instance and T3's group is a 3.
If it cant complete the join to either of the T2 or T3 instances, the record is done for consideration, and since indexes work great for joins like this, make sure you have one index for your NUMBER criteria, and another index on the (GROUP, NUMBER) for those comparisons and the next query sample...
If doing by more than this simple 2, but larger group, prequery qualified groups, then join to that
select
YT2.*
from
( select YT1.group
from YourTable YT1
where YT1.Number in (2, 3)
group by YT1.group
having count( DISTINCT YT1.group ) = 2 ) PreQualified
JOIN YourTable YT2
on PreQualified.group = YT2.group
AND YT2.Number in (2,3)

Maybe this,if I understand you
SELECT id FROM table WHERE `group` IN
(SELECT `group` FROM table WHERE number IN (2,3)
GROUP BY `group`
HAVING COUNT(DISTINCT number)=2)
SQL Fiddle
This will return all ids where BOTH numbers exist in a group.Remove DISTINCT if you want ids for groups where just one numbers is in.

Related

MySQL Output column based on value that corresponds with max ID

There are a lot of questions dealing with max values but I can't find any that relate to this issue.
ID | Company | Result
----------------------
1 | 1 | A
2 | 1 | C
3 | 1 | B <--
4 | 2 | C
5 | 2 | B
6 | 2 | A <!--
7 | 3 | C
8 | 3 | A
9 | 3 | B <--
I need to output the Companies whose last Result (based on ID) was "B".
To further complicate the issue, the $query will be used this:
select * from table where Company in ($query)
Any ideas? Thanks!
On MySQL 8+, here is a query you may try using analytic functions:
WITH cte AS (
SELECT *, ROW_NUMBER() OVER (PARTITION BY Company ORDER BY ID DESC) rn
FROM yourTable
)
SELECT ID, Company, Result
FROM cte
WHERE rn = 1 AND Result = 'B';
Demo
On earlier versions of MySQL, we can try joining to a subquery which finds the most recent record for each company:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT Company, MAX(ID) AS MAX_ID
FROM yourTable
GROUP BY Company
) t2
ON t1.Company = t2.Company AND
t1.ID = t2.MAX_ID
WHERE
t1.Result = 'B';
Demo

Create new columns for duplicate row values based on column ID duplicate in sql

I have a table with 2 columns, the first column is called ID and the second is called TRACKING. The ID column has duplicates, I want to to take all of those duplicates and consolidate them into one row where each value from TRACKING from the duplicate row is placed into a new column within the same row and I no longer have duplicates.
I have tried a few suggested things where all of the values would be concatenated into one column but I want these TRACKING values for the duplicate IDs to be in separate columns. The code below did not do what I intended it to.
SELECT ID, TRACKING =
STUFF((SELECT DISTINCT ', ' + TRACKING
FROM #t b
WHERE b.ID = a.ID
FOR XML PATH('')), 1, 2, '')
FROM #t a
GROUP BY ID
I am looking to take this:
| ID | TRACKING |
-----------------
| 5 | 13t3in3i |
| 5 | g13g13gg |
| 3 | egqegqgq |
| 2 | 14y2y24y |
| 2 | 42yy44yy |
| 5 | 8i535i35 |
And turn it into this:
| ID | TRACKING | TRACKING1 | TRACKING2 |
-----------------
| 5 | 13t3in3i | g13g13gg | 8i535i35 |
| 3 | egqegqgq | | |
| 2 | 14y2y24y | 42yy44yy | |
On (relatively) painful way to do this in MySQL is to use correlated subqueries:
select i.id,
(select t.tracking
from t
where t.id = i.id
order by t.tracking
limit 1, 0
) as tracking_1,
(select t.tracking
from t
where t.id = i.id
order by t.tracking
limit 1, 1
) as tracking_2,
(select t.tracking
from t
where t.id = i.id
order by t.tracking
limit 1, 2
) as tracking_3
from (select distinct id from t
) i;
As bad as this looks, it will probably have surprisingly decent performance with an index on (id, tracking).
By the way, your original code with stuff() would put everything into one column:
select id, group_concat(tracking)
from t
group by id;
with test_tbl as
(
select 5 id, 'goog' tracking,'goog' tracking1
union all
select 5 id, 'goog1','goo'
union all
select 2 , 'yahoo','yah'
union all
select 2, 'yahoo1','ya'
union all
select 3,'azure','azu'
), modified_tbl as
(
select id,array_agg(concat(tracking)) Tracking,array_agg(concat(tracking1)) Tracking1 from test_tbl group by 1
)
select id, tracking[safe_offset(0)] Tracking_1,tracking1[safe_offset(0)] Tracking_2, tracking[safe_offset(1)] Tracking_3,tracking1[safe_offset(1)] Tracking_4 from modified_tbl where array_length(Tracking) > 1

How to select all rows from group, until occurrence of a value

I'm trying to extract all rows from same Group until I hit breakpoint value B. The example data below is ordered virtual table:
+----+--------+------------+
| ID | Group | Breakpoint |
+----+--------+------------+
| 1 | 1 | A |
| 2 | 1 | A |
| 3 | 1 | B |
| 4 | 1 | A |
| 5 | 2 | A |
| 6 | 2 | A |
| 7 | 2 | A |
| 8 | 3 | A |
| 9 | 3 | B |
+----+--------+------------+
This would be my result.
+----+--------+------------+
| ID | Group | Breakpoint |
+----+--------+------------+
| 1 | 1 | A |
| 2 | 1 | A |
| 5 | 2 | A |
| 6 | 2 | A |
| 7 | 2 | A |
| 8 | 3 | A |
+----+--------+------------+
Notice that when there are both A and B breakpoint values within a group, I want to have the rows until the first A value in this order. If there are only A values for a group like in group 2, I want to have all of the items in the group.
Here's a simple solution that uses no subqueries or GROUP BY logic.
SELECT t1.ID, t1.Group, t1.Breakpoint
FROM MyTable AS t1
LEFT OUTER JOIN MyTable AS t2
ON t1.ID >= t2.ID AND t1.`Group` = t2.`Group` AND t2.Breakpoint = 'B'
WHERE t2.ID IS NULL
For each row t1, try to find another row t2 with 'B', in the same Group, with an earlier ID. If none is found, the OUTER JOIN guarantees that t2.ID is NULL. That will be true only up until the desired breakpoint.
From you example above, you are not really grouping the results. you just need to display the records where Breakpoint is A:
Select * From Table
Where Breakpint ='A'
You may use NOT EXISTS
select *
from your_table t1
where not exists (
select 1
from your_table t2
where t1.group = t2.group and t2.id <= t1.id and t2.breakpoint = 'B'
)
or ALL can work as well if you never have NULL in id
select *
from your_table t1
where t1.id < ALL(
select t2.id
from your_table t2
where t1.group = t2.group and t2.breakpoint = 'B'
)
Assuming that we are ordering by ID column, we could do something like this:
SELECT d.*
FROM mytable d
LEFT
JOIN ( SELECT bp.group
, MIN(bp.id) AS bp_id
FROM mytable bp
WHERE bp.breakpoint = 'B'
GROUP BY bp.group
) b
ON b.group = d.group
WHERE b.bp_id > d.id OR b.bp_id IS NULL
ORDER BY d.group, d.id
This takes into account cases where there is no breakpoint='B' row for a given group, and returns all of the rows for that group.
Note that the inline view b gets us the lowest id value from rows with breakpoint='B' for each group. We can outer join that to original table (matching on group), and then conditional tests in the WHERE clause to exclude rows that follow the first breakpoint='B' for each group.
SQL tables represent unordered sets. Hence, there is no "before" or "after" a particular row.
Let me assume that you have some column that specifies the ordering. I'll call it id. You can then do what you want with:
select t.*
from t
where t.id < (select min(t2.id) from t t2 where t2.group = t.group and t2.breakpoint = 'B');
To get all rows when if there are no 'B':
select t.*
from t
where t.id < (select coalesce(min(t2.id), t.id + 1) from t t2 where t2.group = t.group and t2.breakpoint = 'B');

Mysql select with IN, limit rows to 1 for each match

lets say I have this table:
| id | record_id | date_updated |
|----|-----------|--------------|
| 1 | 1 | 19-03-2015 |
| 2 | 1 | 18-03-2015 |
| 3 | 1 | 17-03-2014 |
| 4 | 2 | 01-01-2015 |
| 5 | 2 | 05-02-2015 |
so the results I am looking for are :
| id | record_id | date_updated |
|----|-----------|--------------|
| 1 | 1 | 19-03-2015 |
| 4 | 2 | 01-01-2015 |
I have array with record ids.
$records = [1,2];
So I can do something like:
select * from `mytable`
WHERE `record_id` IN ($records)
AND mytable.date_update > 01-01-2014
AND mytable.date_updated < 12-12-2015
so mysql will select records wich match date_updated criteria ( and record id ofc ), which are more then 1 for each record ID, basically I want to make him limit the rows for each $record_id to 1
If it is even possible.
//it is super hard to explain the problem, the real case is that this is a sub query of another query, but the real example is 10 rows query and 100 columns table, so it will be even more hard to explain the situation and for someone to read it / udnerstands it. Hopefully someone will understand my problem, if not I will try to explain more.
Thanks
You can try using the group by clause
SELECT *
FROM `mytable`
WHERE id IN (
SELECT min(id)
FROM `mytable`
WHERE `record_id` IN ($records)
AND mytable.date_update > 01-01-2014
AND mytable.date_updated < 12-12-2015
group by record_id
);
There are many ways to get the record per group, and since you need only once you can easily do as below
select t1.* from table_name t1
where (
select count(*) from table_name t2
where t1.record_id = t2.record_id
) > =0
and
t1.date_updated > '2014-01-01' and date_updated < '2015-12-12'
group by t1.record_id ;
There are other way too using left join
select t1.* from table_name t1
left join table_name t2 on t1.record_id = t2.record_id
and t1.id >t2.id where t2.id is null
This will give you data with asc order with id
If you need data with max(id) for a record_id you can use
t1.id < t2.id
instead of
t1.id >t2.id
The same comparison you can do with first query.

Complex sort and grouping with mysql

I have the ff table:
---------------------------
ID | ChapterNo | HitCount |
---------------------------
1 | 2 | 1000 |
2 | 2 | 2000 |
3 | 1 | 3000 |
4 | 3 | 1000 |
5 | 1 | 3500 |
---------------------------
Basically I need to archive this result:
Get all the unique chapterno where each have the highest hit count and then order by chapterno descending
ID | ChapterNo | HitCount |
---------------------------
4 | 3 | 1000 |
2 | 2 | 2000 |
5 | 1 | 3500 |
---------------------------
I tried the ff. query:
SELECT t1.*, Max(t1.hitcount) AS maxhit
FROM chapter as t1
GROUP BY t1.chapterno
ORDER BY t1.chapterno DESC
But some how it doesnt return the one with highest hitcount.
How can I fix this?
Thank you
SELECT t1.*, t1.hitcount AS maxhit
FROM chapter as t1
WHERE t1.hitcount = (select max(hitcount) from chapter where chapterno = t1.chapterno)
ORDER BY t1.chapterno DESC
SELECT t1.id, t1.chapterno, t2.maxhits
FROM chapter as t1,
(SELECT id, chapterno, Max(hitcount) AS maxhits
FROM chapter
GROUP BY chapterno) AS t2
WHERE t2.chapterno = t1.chapterno
AND t1.hitcount = t2.maxhits
ORDER BY t1.chapterno DESC
Try this one -
SELECT c1.id, c1.ChapterNo, c1.HitCount FROM chapter c1
JOIN (SELECT ChapterNo, MAX(HitCount) max_hitCount
FROM chapter
GROUP BY ChapterNo) c2
ON c1.ChapterNo = c2.ChapterNo AND c1.HitCount = c2.max_hitCount
ORDER BY c1.ChapterNo DESC;
SELECT t1.*, t1.hitcount AS maxhit
FROM chapter as t1
WHERE t1.hitcount = (
SELECT MAX t1.hitcount
from chapter as t2
where t2.ChapterNo = t1.chapterNo
)
ORDER BY t1.chapterno DESC
This uses a correlated subquery, which can become unefficient. Another possibility is to use an uncorrelated query in the from or left join.
More info on this article
although all above answers are perfect, i think it also can be done using SELF JOIN
SELECT *
FROM chapter ch
WHERE (
SELECT COUNT(*) FROM chapter ch2
WHERE ch2.chapterno = ch.chapterno and ch2.hitcount > ch.hitcount
) <= 2;