First, sorry if the title is confusing.
SQL is not my strong suit and I've been working on this for a while, my thoughts at the mmoment is something with a join, and group maybe.
Soto Example:
record | type | key1 | key2 | data1
---------------------------------------
1 | 1 | joe | smoe | 10
2 | 2 | homer | simpson | 20
3 | 1 | null | null | 30
4 | 3 | bart | simpson | 40
Where primary key is made up of id, key1, key2.
I only want rows of 'type' WHERE key1 is not null AND key2 is not null.
So since in record 3, type 1 has null keys, I therefore want all records of type 1 to not be included in the derived table.
Here's a correlated, "not exists" approach:
select *
from T as t1
where not exists (
select *
from T as t2
where t2.type = t1.type and (t2.key1 is null or t2.key2 is null)
)
And here's one that uses a non-correlated query along with grouping. Perhaps it's what you had in mind:
select *
from T as t1
where t1.type in (
select t2.type
from T as t2
group by t2.type
having count(*) = count(t2.key1) and count(*) = count(t2.key2)
)
Since I understand mysql query plans can be sensitive to these things. Here's the equivalent with a join:
select t1.*
from T as t1
inner join
(
select t2.type
from T as t2
group by t2.type
having count(*) = count(t2.key1) and count(*) = count(t2.key2)
) as goodtypes
on goodtypes.type = t1.type
Related
I have two tables with a VARCHAR column called "name1" and "name2":
table1:
id | name1
1 | xyz
2 | foo
3 | barfoo
4 | xchad
table2:
id | id_table1 | name2
1 | NULL | xchad
2 | NULL | foo
3 | NULL | hade
4 | NULL | bar
I want to update the column id_table1 of table2 with the respective id from table1 where the rows name1 and name2 match.
For example in table2 the first row should be updated with 4 in column id_table1 since 'xchad' = 'xchad'.
A join simply takes too much time with the string compare.
Thank you!
Consider:
UPDATE table1 t1
INNER JOIN table2 t2 ON t2.name2 = t1.name1
SET t2.id_table1 = t1.id
With indexes on table1(name1) and table2(name2), this should perform efficiently.
An alternative is to use a correlated subquery:
UPDATE table2 t2
SET t2.id_table1 = (
SELECT t1.name1 FROM table1 t1 WHERE t1.name1 = t2.id_table1
)
Please note that this second solution does require each name in table2 to have a unique match in table1.
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');
I have 2 tables:
T1:
id | name
------ | ------
1 | Bob
2 | John
3 | Joe
T2:
id | T1_id | type
------ | ------ | ------
1 | 1 | call
2 | 1 | email
3 | 1 | fax
4 | 2 | call
5 | 2 | email
6 | 2 | fax
7 | 3 | call
8 | 3 | email
I want to count the number of records in T1 which do not have a record in T2 with a type of 'fax'.
So the answer in this case would be 1 (3|Joe)
Currently I have:
SELECT count(*)
FROM `T1`
JOIN `T2` on `T1`.`id` = `T2`.`T1_id`
WHERE `T2`.`type` != 'fax'
But this is obviously counting all the records which are not 'fax'. I just cant get the logic in my head.
Any help would be appreciated!
A subquery is unnecessary:
SELECT COUNT(DISTINCT t1.id)
FROM t1
LEFT
JOIN t2
ON t2.t1_id = t1.id
AND t2.type = 'fax'
WHERE t2.id IS NULL;
select count(*)
from
(
SELECT t1.id
FROM T1
LEFT JOIN T2 on T1.id = T2.T1_id
GROUP BY t1.id
HAVING sum(T2.type = 'fax') = 0
) tmp
The answers given by Strawberry and juergen d are correct, but for completeness, here's another example using NOT EXISTS. All the queries will have different execution plans, so depending on your data in T1 and T2 YMMV:
SELECT COUNT(*)
FROM `T1`
WHERE NOT EXISTS (
SELECT *
FROM `T2`
WHERE `T2`.`T1_id` = `T1`.`id`
AND `T2`.`type` = 'fax'
)
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.
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.