How to select in 1 query below. This query need re search that's find value to their own loop.
This is different from other sub query , using 1 table only
TAble T
| num| WHOSE
| 1 | A
| 1 | C
| 2 | B
| 2 | C
| 3 | D
Criteria to match records (conditions):
The value in column whose is not C
The value in column num does not match a value for another record in condition 1.
I want to find the record the value 3 in column num (which has D for column whose).
select * from T where whose <> C and ( num is not one of c's)
1 A can not because C has 1
2 B can not because C has 2
3 D is what I want, because it doesn't have C in column whose nor share a value in column num with a record that does have C in the column whose.
First select num of those records where whose is C. Then select those records where whose is not C and also where num is not one of the ones in subquery.
Select * from T where whose <> 'C' and num not in (Select Num from T where whose = 'C' )
Another way to achieve the same result is with a LEFT JOIN on the same table:
SELECT T.*
FROM T
LEFT JOIN T t2 on t2.num = T.num and t2.whose = 'C'
WHERE T.whose <> 'C' AND t2.whose IS NULL
Check it out on this SQL Fiddle, where the result is:
| num | whose |
| 3 | D |
Additionally, a similar way to write the query is to use the NOT EXISTS clause in the WHERE conditions, like this:
SELECT T.* from T
WHERE T.whose <> 'C' AND NOT EXISTS (SELECT 1 FROM T t2 WHERE
t2.num = T.num AND t2.whose = 'C')
Check it out in this SQL fiddle.
To read more about the comparison between EXISTS and LEFT JOIN see this article. In the summary at the end it has the following conclusions:
MySQL can optimize all three methods to do a sort of NESTED LOOPS ANTI JOIN.
...
However, these three methods generate three different plans which are executed by three different pieces of code. The code that executes EXISTS predicate is about 30% less efficient than those that execute index_subquery and LEFT JOIN optimized to use Not exists method.
That's why the best way to search for missing values in MySQL is using a LEFT JOIN / IS NULL or NOT IN rather than NOT EXISTS.
Related
I've large data which could be filtered on type column. I can get rows data using subquery with self-join and transform to the column, but due to large data, it would take a lot of time using a subquery. My table structure is like:
Table A
Id | ItemName | Type
1 | Item1 | A
2 | Item2 | A
3 | Item3 | B
4 | Item4 | B
Table B
Id | ItemDescTag | Ext
1 | ItemTag | A
4 | ItemTag | B
Query
Select ItemName,PurchaseOrdr.Quantity, PurchaseOrdr.PORate, PurchaseOrdr.CostAmount
from TableA its
left join (
select it.Id, it.Quantity,(it.CostAmount/it.Quantity)as PORate, it.CostAmount from TableA it
inner join Table B m on it.Type = convert(int ,m.Ext)
)as PurchaseOrdr on PurchaseOrdr.Id = its.Id
I need an alternate way to get rid of this SUB-QUERY because I have to make 10 SUB-QUERY of a similar type to get the required data and due to millions of records it slows down ...
Depending on how similar the other 10 sub-queries are you could query the columns needed ahead of time in CTE like
;WITH PurchaseOrdr_CTE AS(
SELECT
it.Id,
it.Quantity,
it.CostAmount
FROM TableA it
JOIN TableB m
ON it.Type = convert(int ,m.Ext)
)
SELECT
ItemName,
PurchaseOrdr.Quantity
, (PurchaseOrdr.CostAmount/PurchaseOrdr.Quantity) AS PORate,
PurchaseOrdr.CostAmount
FROM TableA its
LEFT JOIN PurchaseOrdr_CTE PurchaseOrdr
ON its.Id = PurchaseOrdr.Id
In this particular example I don't see the need to reference TableA twice at all. But the actual query you are using might have additional columns, logic not included in the post.
The it.Type = convert(int ,m.Ext) conversion will have some overhead. If it's possible to use consistent data types that's obviously better.
If you are dealing with millions of records and this will be an important query, a good covering index will have a large impact.
situation:
table 1 - #__virtuemart_products
virtuemart_product_id | product_special
PRODUCTS_IDS | 0 or 1
table 2 - #__virtuemart_product_badges
virtuemart_product_id | product_badge
PRODUCTS_IDS | for this situation code 3
I have a default SQL
SELECT p.`virtuemart_product_id`
FROM `#__virtuemart_products` as p
WHERE p.`product_special` = 1;
results is product IDs like 2,3,225,...
I need modify this SQL syntax for select IDs from 2 different tables and return one column.
If I modify syntax like that:
SELECT p.`virtuemart_product_id`, badges_table.`virtuemart_product_id`
FROM `#__virtuemart_products` as p, `#__virtuemart_product_badges` as badges_table
WHERE p.`product_special` = 1 OR badges_table.`badge` = 3
Result is:
virtuemart_product_id | virtuemart_product_id
1 | 123
1 | 321
1 | 231
....
why is first column 1,1,1,...? here must be product_id, no product_special code
I need group this results into one column virtuemart_product_id
What I doing wrong?
I think what you are looking for is UNION of the IDs fetched from two different tables.
SELECT p.`virtuemart_product_id`, badges_table.`virtuemart_product_id`
FROM `#__virtuemart_products` as p, `#__virtuemart_product_badges` as
badges_table
WHERE p.`product_special` = 1 OR badges_table.`badge` = 3
What the above query is doing is, it is performing a join between the two tables with the condition that product_special should be 1 or badge should be 3. Hence, each row from one table will be joined with each row of the other table where the condition will satisfy.
To get IDs from both the tables you can get the results from each table according to condition and then perform a UNION on them. So for example
(SELECT `virtuemart_product_id` FROM `#__virtuemart_products` WHERE
`product_special` = 1)
UNION
(SELECT `virtuemart_product_id` FROM
`#__virtuemart_product_badges` WHERE `badge` = 3)
I hope this helps.
I have a table like this
itemid | propertyname | propertyvalue
___________|______________|_______________
1 | point | 12
1 | age | 10
2 | point | 15
2 | age | 11
3 | point | 9
3 | age | 10
4 | point | 13
4 | age | 11
I need a query to select all items where age greater than 10 and point less than 12.
I tried
`select itemid from table where (propertyname="point" and propertyvalue < 12)
and (propertyname="age" and propertyvalue >10)`
It gives no results. How can I make it work?
you can use an inner join
SELECT
a.itemid
FROM
yourTable a
INNER JOIN
yourTable b
ON
a.itemid=b.itemid
AND a.propertyname='point'
AND b.propertyname='age'
WHERE
a.propertyvalue<12
AND b.propertyvalue>10
ok so in table a youre lookin for all items with the name point and a value smaller 12 and in table b youre looking for all items with the name age and a value greater 10. Then you only have to look for items, which are in both tables. For this you connect the two tables over the itemid. To connect tables you use the join. Hope this will help you to understand. If not ask again :)
To join a table to itself in the same query you can include the table twice in the FROM clause, giving it a different alias each time. Then you simply proceed with building your query as if you were dealing with two separate tables that just happen to contain exactly the same data.
In the query below the table example is aliased as a and b:
SELECT a.itemid
FROM example a, example b
WHERE a.itemid = b.itemid
AND a.propertyname = 'point'
AND a.propertyvalue < 12
AND b.propertyname = 'age'
AND b.propertyname > 10
Try It:
SELECT itemid FROM test_table WHERE propertyname="point" AND propertyvalue < 12 AND itemid IN(SELECT itemid FROM test_table WHERE propertyname="age" AND propertyvalue >10)
http://sqlfiddle.com/#!9/4eafc6/1
PLs Try this
select itemid from table where (propertyname="point" and propertyvalue < 12)
or (propertyname="age" and propertyvalue >10);
Here's one idea...
SELECT item_id
, MAX(CASE WHEN propertyname = 'point' THEN propertyvalue END point
, MAX(CASE WHEN propertyname = 'age' THEN propertyvalue END age
FROM a_table
GROUP
BY item_id
HAVING age+0 > 10
AND point+0 < 12;
You can use an inner join. Meaning, it's like you're going to work with 2 tables: the first one you're going to select the name="age" and val>10, and the second one is where you're going to select name="point" and val<12.
It's like you're creating an instance of your table that doesn't really exist. It's just going to help you extract the data you need at the same time.
I need to merge to queries together. This is a simple task that I do a lot, but this time I have something a little bizarre. The 2 queries I'm merging are from the same table and the same column.
Here is a basic concept of my original table:
|id|content|
0 | a
1 | b
2 | c
3 | d
I want my query to return everything once. Then again where I selected it. So SELECT * FROM 'my_table' and SELECT * FROM 'my_table WHERE id IN (1,2) I want it to return something like this.
a
b
a
b
c
d
Edit: #dnoeth commented the right answer. I didn't read it right the first time though.
I found the answer I was looking for. I just didn't know how to word was I was looking for.
What I needed to do was unite 2 queries on the same table.
You do this by simply using UNION ALL instead of union
So here is an example in mysql.
SELECT * FROM my_table UNION ALL SELECT * FROM my_table WHERE id IN (1,2)
Then if you are using Laravel's eloquent like I am use this.
$query = Class::whereIn('id', $array);
Class::where('id','>=',0)->unionAll($query)->get();
Well since you're not really precise on what the result should be, you might do like this:
select t1.val
from tableT t1
join tableT t2 on t1.id=t2.id or t1.id+1=t2.id
where t1.id<=2 and t2.id<=2 or t1.id>=3
Should yield:
a
b
b
c
c
d
Just use the clause UNION, you can "join" several SELECTs with the only restiction that they have same result columns. Your example will be:
(SELECT * FROM 'my_table')
UNION
(SELECT * FROM 'my_table WHERE id IN (1,2))
And you will get:
|id|content|
0 | a
1 | b
2 | c
3 | d
1 | b
2 | c
More details:
http://dev.mysql.com/doc/refman/5.7/en/union.html
PS:
The default behavior for UNION is that duplicate rows are removed from the result. With the optional ALL keyword, duplicate-row removal does not occur and the result includes all matching rows from all the SELECT statements.
Editted heavily!
The original question was based on a misunderstanding of how IN() treats a column from a results set from a join. I thought IN( some_join.some_column ) would treat a results column as a list and loop through each row in place. It turns out it only looks at the first row.
So, the adapted question: Is there anything in MySQL that can loop through a column of results from a join from a WHERE clause?
Here's the super-simplified code I'm working with, stripped down from a complex crm search function. The left join and general idea are relics from that query. So for this query, it has to be an exclusive search - finding people with ALL specified tags, not just any.
First the DB
Table 1: Person
+----+------+
| id | name |
+----+------+
| 1 | Bob |
| 2 | Jill |
+----+------+
Table 2: Tag
+-----------+--------+
| person_id | tag_id |
+-----------+--------+
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 2 | 3 |
+-----------+--------+
Nice and simple. So, naturally:
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id GROUP BY name;
+------+--------------------------+
| name | GROUP_CONCAT(tag.tag_id) |
+------+--------------------------+
| Bob | 1,2 |
| Jill | 2,3 |
+------+--------------------------+
So far so good. So what I'm looking for is something that would find only Bob in the first case and only Jill in the second - without using HAVING COUNT(DISTINCT ...) because that doesn't work in the broader query (there's a seperate tags inheritance cache and a ton of other stuff).
Here's my original sample queries - based on the false idea that IN() would loop through all rows at once.
SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id
WHERE ( ( 1 IN (tag.tag_id) ) AND ( 2 IN (tag.tag_id) ) );
Empty set (0.00 sec)
SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id
WHERE ( ( 2 IN (tag.tag_id) ) AND ( 3 IN (tag.tag_id) ) );
Empty set (0.00 sec)
Here's my new latest failed attempt to give an idea of what I'm aiming for...
SELECT name, GROUP_CONCAT(tag.tag_id) FROM person LEFT JOIN tag ON person.id = tag.person_id
GROUP BY person.id HAVING ( ( 1 IN (GROUP_CONCAT(tag.tag_id) ) ) ) AND ( 2 IN (GROUP_CONCAT(tag.tag_id)) );
Empty set (0.00 sec)
So it seems it's taking a GROUP_CONCAT string, of either 1,2 or 2,3, and is treating it as a single entity rather than an expression list. Is there any way to turn a grouped column into an expression list that IN () or =ANY() will treat as a list?
Essentially, I'm trying to make IN() loop iteratively over something that resembles an array or a dynamic expression list, which contains all the rows of data that come from a join.
Think about what your code is doing logically:
( 1 IN (tag.tag_id) ) AND ( 2 IN (tag.tag_id) )
is equivalent to
( 1 = (tag.tag_id) ) AND (2 = (tag.tag_id) )
There's no way tag.tag_id can satisfy both conditions at the same time, so the AND is never true.
It looks like the OR version you cited in your question is the one you really want:
SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id
WHERE ( ( 1 IN (tag.tag_id) ) OR ( 2 IN (tag.tag_id) ) );
Using the IN clause more appropriately, you could write that as:
SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id
WHERE tag.tag_id in (1,2);
One final note, because you're referencing a column from the LEFT JOINed table in your WHERE clause (tag.tag_id), you're really forcing that to behave like an INNER JOIN. To truly get a LEFT JOIN, you'd need to move the criteria out of the WHERE and make it part of the JOIN conditions instead:
SELECT DISTINCT name FROM person LEFT JOIN tag ON person.id = tag.person_id
AND tag.tag_id in (1,2);
WHERE ( ( 1 IN (tag.tag_id) ) AND ( 2 IN (tag.tag_id) ) );
This will never return any results since tag.tag_id cannot be 1 and 2 at the same time.
Additionally is there a reason you're using 1 IN (blah) rather than blah = 1?