Into a very high selective MySQL query, is there a way to know which condition did not match?
Eg.: If I'm looking for 12 conditions and my MySQL returns 0, wich one of these 12 conditions did not match?
SELECT
article.id,
article.name,
GROUP_CONCAT(tags.name order by tags.name) AS nameTags,
GROUP_CONCAT(tags.id order by tags.id) AS idTags,
MAX(IF(article.name LIKE '%var1%',1,0)) AS var1match,
MAX(IF(article.name LIKE '%var2%',1,0)) AS var2match,
and more 10 conditions.
FROM
article
LEFT JOIN ....
LEFT JOIN ....
GROUP BY id
HAVING
(nameTags LIKE '%var1%' OR var1match=1)
AND (nameTags LIKE '%var2%' OR var2match=1)
AND more 10 conditions.
I mean, do it without make 12 single consults to see which one results nothing.
Update: Got some evolution
If I do this:
SELECT
article.id,
MAX(IF(article.name LIKE '%var1%',1,0) AS var1,
MAX(IF(article.name LIKE '%var2%',1,0) AS var2,
MAX(IF(article.name LIKE '%var3%',1,0) AS var3,
FROM
article
LEFT JOIN ....
LEFT JOIN ....
GROUP BY article.id
I'll get an aliased table like this
|id|var1|var2|var3|
| 1| 0| 0| 1|
| 2| 0| 1| 0|
| 3| 0| 0| 0|
| 4| 0| 1| 1|
So I know that 'var 1' doesn't match with the condition because all column value is equal 0. How I get the return of that column name?
Tried to sum value and get columns that sum = 0. Don't know how to do.
Update:
Found to do that is using temporary tables.
1) Create tmp table:
CREATE TEMPORARY TABLE tmp_tagReport AS
SELECT
article.id,
MAX(IF(article.name LIKE '%var1%',1,0) AS l_var1,
MAX(IF(article.name LIKE '%var2%',1,0) AS l_var2,
MAX(IF(article.name LIKE '%var3%',1,0) AS l_var3,
FROM
article
LEFT JOIN ....
LEFT JOIN ....
GROUP BY article.id
You'll have something like this:
|id|l_var1|l_var2|l_var3|
| 1| 0| 0| 1|
| 2| 0| 1| 0|
| 3| 0| 0| 0|
| 4| 0| 1| 1|
2) Sum values and you will know that column with 0 has no match
SELECT
SUM(l_var1) as l_var1,
SUM(l_var2) as l_var2,
SUM(l_var3) as l_var3
You'll get something like this:
|l_var1|l_var2|l_var3|
| 0| 2| 2|
3) Get name of columns with value = 0 in PHP
$querySelect = "SELECT
SUM(l_var1) as l_var1,
SUM(l_var2) as l_var2,
SUM(l_var3) as l_var3
";
$result = mysql_query($querySelect);
$arrayUnfound = array_keys(#mysql_fetch_assoc($result), "0");
print_r($arrayUnfound)
4) It is a temporary table, but I added a drop table just to make sure and try to free memory:
mysql_query("DROP TABLE tmp_tagReport; ");
I found myself a way to do it, but it still has to make 2 or more queries. However I hope it could be helpful.
sorry my english
Thanks
Related
I have two tables. One contains offers with amount of items, and second contains allocations of items to offers. I am trying to write a query that produces table showing offered amount of items and total allocated amount, per offer.
Sample table `offers`:
+----+----+----+
|oid |uid |amt |
+----+----+----+
| 1| 413| 10|
| 2| 297| 7|
+----+----+----+
Sample table `allocations`:
+----+----+------+
|aid |oid |alloc |
+----+----+------+
| 1| 2| 4|
| 2| 2| 2|
+----+----+------+
I want the following result:
+----+----+------+
|oid |amt |alloc |
+----+----+------+
| 1| 10| 0|
| 2| 7| 6|
+----+----+------+
I tried the following query:
SELECT `offers`.`oid`, `offers`.`amt`, COALESCE(SUM(`allocations`.`alloc`),0)
FROM `offers`
LEFT JOIN `allocations` ON `allocations`.`oid`=`offers`.`oid`
However, the sum works on the whole table, not just entries that satisfy
`allocations`.`oid`=`offers`.`oid`
and offers that have no allocations don't get printed.
Try using GROUP BY.
SELECT `offers`.`oid`, `offers`.`amt`, COALESCE(SUM(`allocations`.`alloc`),0)
FROM `offers`
LEFT JOIN `allocations` ON `allocations`.`oid`=`offers`.`oid`
GROUP BY offers.oid
(I assume offers.oid is your primary key?)
Strictly speaking, it should be
GROUP BY offers.oid, offers.amt
which is to say any column not the result of an aggregate function must be listed in the GROUP BY clause. Some servers are configured to enforce this, while some aren't.
Any aggregate function, such as sum() will operate in the entire resultset, unless you provide a group by clause.
SELECT `offers`.`oid`, `offers`.`amt`, COALESCE(SUM(`allocations`.`alloc`),0)
FROM `offers`
LEFT JOIN `allocations` ON `allocations`.`oid`=`offers`.`oid`
GROUP BY `offers`.`oid`, `offers`.`amt`
However, I do not get the 'offers that do not have allocations do not get printed' part, since the left join should take care of that, unless in your real query the order of the 2 tables are reversed.
So here you have to use group by clause to do sum for each oid...
SELECT `offers`.`oid`, `offers`.`amt`, COALESCE(SUM(`allocations`.`alloc`),0) FROM `offers`
LEFT JOIN `allocations` ON `allocations`.`oid`=`offers`.`oid` group by oid
I have the following (simplified) table:
------------------
|store|item|value|
------------------
| 1| 1| 4|
| 1| 2| 3|
| 1| 3| 0|
| 2| 2| 2|
| 2| 3| 1|
| 3| 4| 2|
I want to know which stores are missing any item from a list of item, and I want to know the value for the items they do have. The list of items is normally larger than above, but is usually only searched by a few at a time.
So let's say I want to know which stores are missing any of item 1 or item 2. I would like results such as this ideally:
-------------
|store| 1| 2|
-------------
| 2| 0| 2|
| 3| 0| 0|
Store 1 is not returned because it has entries for both item 1 and item 2 where the value is greater than 0 for each. Initially I only needed the store ids but putting the items as columns with their values in the rows would be very helpful for what we need now. I started out with a monstrosity of 3 queries union'd together that worked okay to give the list of stores (first column). As I was thinking about how to add the other columns I decided to see if I could simplify the query. I came up with this:
select store
from table t1
where
(select count(*)
from (
select store,item
from table t2
where t2.item in (1,2) and value != 0
group by item,store
) t3
where t3.store = t1.store
) != 2
It does not perform as well as my first longer query though, and it doesn't give me the columns I'd like. The idea was to get a count of each store's matching unique items and if that count did not match the number of items queried for then return that store because it was missing at least one item from the list.
Any pointers or help on how to achieve the desired results would be appreciated!
If you want one row per store, then you can use conditional aggregation:
select store,
sum(case when item = 1 then value else 0 end) as item1,
sum(case when item = 2 then value else 0 end) as item2
from table
group by store
having least(item1, item2) = 0;
This assumes that all stores have at least one item of any type.
I have got 3 tables:
+-----+----------+ +-----+----------+-------+ +-----+----------+-------+
| id | A_id | | A_id| B_id | value | | B_id| B_id_ | value |
+-----+----------+ +-----+----------+-------+ +-----+----------+-------+
| 1| 5| | 5| 1| aa| | 1| 2| zzxx|
+-----+----------+ +-----+----------+-------+ +-----+----------+-------+
| 2| 3| | 3| 3| bb| | 2| | vvyy|
+-----+----------+ +-----+----------+-------+ +-----+----------+-------+
| 3| 4| bbll|
+-----+----------+-------+
| 5| | oopp|
+-----+----------+-------+
| 4| 5| mmnn|
+-----+----------+-------+
What SELECT statement i need to use, so that output would look like this(table3 can be up to 4 levels deep into it self):
+----+------------------------------+
| id | value |
+----+------------------------------+
| 1| aa\zzxx\vvyy|
+----+------------------------------+
| 2| bb\bbll\mmnn\oopp|
+----+------------------------------+
As i don't have much experience with DB and SQL, this is hard for me. And I have no vision about how to do this.
This has to be done in MySQL. Hardest thing as i have read is the recursive query in MySQL since it doesn't exist, so people have to simulate it. I have read some SO topics about the recursive Query, but i understood that's not for me.
Any help is appreciated.
By hard and fast learning I managed to solve my problem. Code below.
SELECT DISTINCT
OTHER.DATA,
concat(
'/',ifnull(t4.value,''), CASE WHEN (t4.value is NULL) then '' else '/' END,
ifnull(t3.value,''), CASE WHEN (t3.value is NULL) then '' else '/' END,
ifnull(t2.value,''), CASE WHEN (t2.value is NULL) then '' else '/' END,
ifnull(t1.value,''), CASE WHEN (t1.value is NULL) then '' else '/' END,
table2.value
) as 'My Column name'
FROM
table1
LEFT JOIN table2 ON
(table1.A_id = table2.A_id)
LEFT JOIN table3 as t1 ON
(t1.B_id = table2.B_id)
LEFT JOIN table3 AS t2 ON
(t2.B_id = t1.B_id_)
LEFT JOIN table3 AS t3 ON
(t3.B_id = t2.B_id_)
LEFT JOIN table3 AS t4 ON
(t4.B_id = t3.B_id_)
Big Thanks to #Damodaran and his solution for recursive query.
How to create a MySQL hierarchical recursive query
Be careful with using this code, as I have used it for DB, which is only queried for data. So this approach might be slow on other different usage. If you use this, I suggest you to think about indexing some fields.
In a project I manage invoices that have a status which is changed throughout their lifetime. The status changes are saved in another database table which is similar to this:
|id|invoice_id|user_id|old_status_id|new_status_id|change_date |
-----------------------------------------------------------------------
| 1| 1| 1| 1| 3|2013-11-11 12:00:00|
| 2| 1| 2| 3| 5|2013-11-11 12:30:00|
| 3| 2| 3| 1| 2|2013-11-10 08:00:00|
| 4| 1| 1| 5| 6|2013-11-11 13:10:00|
| 5| 2| 2| 2| 5|2013-11-10 09:00:00|
For each invoice, I would like to retrieve the last status change. Thus the result should contain the records with the ids 4 and 5.
|id|invoice_id|user_id|old_status_id|new_status_id|change_date |
-----------------------------------------------------------------------
| 4| 1| 1| 5| 6|2013-11-11 13:10:00|
| 5| 2| 2| 2| 5|2013-11-10 09:00:00|
If I group by the invoice_id and use max(change_date), I will retrieve the youngest date, but the field values of the other fields are not taken from those records containing the youngest date in the group.
That's challenge #1 for me.
Challenge #2 would be to realize the query with CakePHP's methods, if possible.
Challenge #3 would be to filter the result to those records belonging to the current user. SO if the current user has the id 1, the result is
|id|invoice_id|user_id|old_status_id|new_status_id|change_date |
-----------------------------------------------------------------------
| 4| 1| 1| 5| 6|2013-11-11 13:10:00|
If he or she has user id 2, the result is
|id|invoice_id|user_id|old_status_id|new_status_id|change_date |
-----------------------------------------------------------------------
| 5| 2| 2| 2| 5|2013-11-10 09:00:00|
For the user with id 3 the result would be empty.
In other words, I do not want to find all latest changes that a user has made, regardless whether he was the last one that made a change. Instead, I want to find all invoice changes where that user was the ast one so far who made a change. The motivation is that I want to enable a user to undo his change, which is only possible if no other user after him performed another change.
In case anyone needs an answer
Strictly focusing on:
I want to find all invoice changes where that user was the last one so far who made a change
Write the SQL as
SELECT foo.*
FROM foo
LEFT JOIN foo AS after_foo
ON foo.invoice_id = after_foo.invoice_id
AND foo.change_date < after_foo.change_date
WHERE after_foo.id IS NULL
AND foo.user_id = 1;
Implement using the JOIN clause within Cakephp's find.
The SQL for the suggested algorithm is something like:
SELECT foo.*
FROM foo
JOIN (SELECT invoice_id, MAX(change_date) AS most_recent
FROM foo
GROUP BY invoice_id) AS recently
ON recently.invoice_id = foo.invoice_id
AND recently.most_recent = foo.change_date
WHERE foo.user_id = 1;
Need your help on the following: need to select last three comments for each client and insert it into columns. So, the input looks like this:
ID| Client_ID| Comment_Date| Comments|
1| 1| 29-Apr-13| d|
2| 1| 30-Apr-13| dd|
3| 1| 01-May-13| ddd|
4| 1| 03-May-13| dddd|
5| 2| 02-May-13| a|
6| 2| 04-May-13| aa|
7| 2| 06-May-13| aaa|
8| 3| 03-May-13| b|
9| 3| 06-May-13| bb|
10| 4| 01-May-13| c|
The output I need to get is as follows:
Client_ID| Last comment| (Last-1) comment| (Last-2) comment|
1| dddd| ddd| dd|
2| aaa| aa| a|
3| bb| b|
4| c|
Please, help!!
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.client_id = x.client_id
AND y.id >= x.id
GROUP
BY x.client_id
, x.id
HAVING COUNT(*) <=3;
If don't think you can get this with an SQL request. Maybe you can, but i think it's easier with PHP. For example, you can get your comments with this request :
SELECT * FROM Comment
WHERE Client_ID = ?
LIMIT 0,3
ORDER BY Date DESC
It will return to you the three last comments of an user. Then, you can do whatever you want with that !
Hope it'll help.