This looks like it should be really easy question, but I've been looking for an answer for the past two days and can't find it. Please help!
I have two tables along the lines of
texts.text_id, texts.other_stuff...
pairs.pair_id, pairs.textA, pairs.textB
The second table defines pairs of entries from the first table.
What I need is the reverse of an ordinary LEFT JOIN query like:
SELECT texts.text_id
FROM texts
LEFT JOIN text_pairs
ON texts.text_id = text_pairs.textA
WHERE text_pairs.textB = 123
ORDER BY texts.text_id
How do I get exclusively the texts that are not paired with A given textB? I've tried
WHERE text_pairs.textB != 123 OR WHERE text_pairs.textB IS NULL
However, this returns all the pairs where textB is not 123. So, in a situation like
textA TextB
1 3
1 4
2 4
if I ask for textB != 3, the query returns 1 and 2. I need something that will just give me 1.
The comparison on the second table goes in the ON clause. Then you add a condition to see if there is no match:
SELECT t.text_id
FROM texts t LEFT JOIN
text_pairs tp
ON t.text_id = tp.textA AND tp.textB = 123
WHERE tp.textB IS NULL
ORDER BY t.text_id ;
This logic is often expressed using NOT EXISTS or NOT IN:
select t.*
from texts t
where not exists (select 1
from text_pairs tp
where t.text_id = tp.textA AND tp.textB = 123
);
I have a many to many table setup. This is an example of table I am focusing on.
many_to_many_id, foreign_key_id
1, 1
1, 2
1, 3
2, 1
2, 2
3, 1
3, 4
I need to given many_to_many_id 1 find any other many_to_many_ids that have matching foreign keys that exist within the first set. Given the first set 1, 2, 3 attached to many_to_many_id would return 2 as 1, and 2 are inside the set, but 3 would not be returned as 4 is not part of the test set. My boss has said I should use a dynamic cross tab to create two tables to compare with a join. I have looked for examples but they have not been helpful.
You can do this with a few simple sub-queries. The first will make sure that for each many_to_many_id there is at least one foreign_key_id in the set you're looking for (1, 2, 3) and the second will make sure there isn't even one foreign_key_id not in the set you're looking for.
SET #search_id = 1;
SELECT m.many_to_many_id FROM SampleTable m
WHERE m.many_to_many_id != #search_id
AND EXISTS ( SELECT 1 FROM SampleTable a WHERE a.many_to_many_id = m.many_to_many_id AND a.foreign_key_id IN ( SELECT b.foreign_key_id FROM SampleTable b WHERE b.many_to_many_id = #search_id ) )
AND NOT EXISTS ( SELECT 1 FROM SampleTable a WHERE a.many_to_many_id = m.many_to_many_id AND a.foreign_key_id NOT IN ( SELECT b.foreign_key_id FROM SampleTable b WHERE b.many_to_many_id = #search_id ) )
GROUP BY m.many_to_many_id
SQL Fiddle Here
I come with this problem in a kinda complex query to me. Let me be more clear with an example.
If I have a banner table which stores all the banners on a site and I have another table called banner_statistics which stores a record for each banner event in a particular period of time (the event can be 'I', which means an impression of the banner and 'C', which means a click on a banner).
If I run this query:
-- Count number of impressions of the banner with id = 1
SELECT
ban_id,
ban_url,
COUNT(bansta_event) as num_impressions
FROM banner
LEFT JOIN banner_statistics
ON bansta_ban_id = ban_id
AND bansta_event = 'I'
WHERE ban_id = 1
I get:
ban_id ban_url num_impressions
1 http://www.cocacola.com 7
It says that the banner with id = 1 which is a cocacola banner had a total of 7 impressions.
If I run the same query with the 'C' (click event on that banner):
-- Count number of impressions of the banner with id = 1
SELECT
ban_id,
ban_url,
COUNT(bansta_event) as num_impressions
FROM banner
LEFT JOIN banner_statistics
ON bansta_ban_id = ban_id
AND bansta_event = 'C'
WHERE ban_id = 1
I get:
ban_id ban_url num_clicks
1 http://www.cocacola.com 1
It says that this banner had only one click
Now how can I merge those results into a single query in order to obtain this result:
ban_id ban_url num_impressions num_clicks
1 http://www.cocacola.com 7 1
Is there a way to do it? It seems like a CONCAT but it's not cause it is the result of another query with a COUNT on the same column but where the condition on what to count on that column is different...
How can I achieve this in MySQL? I have found that there is GROUP_CONCAT but it can't receive the result of a SELECT query as param, and I don't think that it can work in my case...
Try this:
SELECT
ban_id,
ban_url,
IFNULL(SUM(bansta_event = 'C'), 0) as num_impressions,
IFNULL(SUM(bansta_event = 'I'), 0) as num_clicks
FROM banner
LEFT JOIN banner_statistics
ON bansta_ban_id = ban_id
AND bansta_event IN ('I', 'C')
WHERE ban_id = 1
In MySQL, comparison operators return 1 when they match, 0 when they don't. So summing them gets the count of rows that match that criteria. The IFNULL is needed in case there are no rows that match that the criteria, so we get 0 instead of NULL in the results.
I have a mysql table which contains some random combination of numbers. For simplicity take the following table as example:
index|n1|n2|n3
1 1 2 3
2 4 10 32
3 3 10 4
4 35 1 2
5 27 1 3
etc
What I want to find out is the number of times a combination has occured in the table. For instance, how many times has the combination of 4 10 or 1 2 or 1 2 3 or 3 10 4 etc occured.
Do I have to create another table that contains all possible combinations and do comparison from there or is there another way to do this?
For a single combination, this is easy:
SELECT COUNT(*)
FROM my_table
WHERE n1 = 3 AND n2 = 10 AND n3 = 4
If you want to do this with multiple combinations, you could create a (temporary) table of them and join that table with you data, something like this:
CREATE TEMPORARY TABLE combinations (
id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
n1 INTEGER, n2 INTEGER, n3 INTEGER
);
INSERT INTO combinations (n1, n2, n3) VALUES
(1, 2, NULL), (4, 10, NULL), (1, 2, 3), (3, 10, 4);
SELECT c.n1, c.n2, c.n3, COUNT(t.id) AS num
FROM combinations AS c
LEFT JOIN my_table AS t
ON (c.n1 = t.n1 OR c.n1 IS NULL)
AND (c.n2 = t.n2 OR c.n2 IS NULL)
AND (c.n3 = t.n3 OR c.n3 IS NULL)
GROUP BY c.id;
(demo on SQLize)
Note that this query as written is not very efficient due to the OR c.n? IS NULL clauses, which MySQL isn't smart enough to optimize. If all your combinations contain the same number of terms, you can leave those out, which will allow the query to make use of indexes on the data table.
Ps. With the query above, the combination (1, 2, NULL) won't match (35, 1, 2). However, (NULL, 1, 2) will, so, if you want both, a simple workaround would be to just include both patterns in your table of combinations.
If you actually have many more columns than shown in your example, and you want to match patterns that occur in any set of consecutive columns, then your really should pack your columns into a string and use a LIKE or REGEXP query. For example, if you concatenate all your data columns into a comma-separated string in a column named data, you could search it like this:
INSERT INTO combinations (pattern) VALUES
('1,2'), ('4,10'), ('1,2,3'), ('3,10,4'), ('7,8,9');
SELECT c.pattern, COUNT(t.id) AS num
FROM combinations AS c
LEFT JOIN my_table AS t
ON CONCAT(',', t.data, ',') LIKE CONCAT('%,', c.pattern, ',%')
GROUP BY c.id;
(demo on SQLize)
You could make this query somewhat faster by making the prefixes and suffixes added with CONCAT() part of the actual data in the tables, but this is still going to be a fairly inefficient query if you have a lot of data to search, because it cannot make use of indexes. If you need to do this kind of substring searching on large datasets efficiently, you may want to use something better suited for than specific purpose than MySQL.
You only have three columns in the table, so you are looking for combinations of 1, 2, and 3 elements.
For simplicity, I'll start with the following table:
select index, n1 as n from t union all
select index, n2 from t union all
select index, n3 from t union all
select distinct index, -1 from t union all
select distinct index, -2 from t
Let's call this "values". Now, we want to get all triples from this table for a given index. In this case, -1 and -2 represent NULL.
select (case when v1.n < 0 then NULL else v1.n end) as n1,
(case when v2.n < 0 then NULL else v2.n end) as n2,
(case when v3.n < 0 then NULL else v3.n end) as n3,
count(*) as NumOccurrences
from values v1 join
values v2
on v1.n < v2.n and v1.index = v2.index join
values v3
on v2.n < v3.n and v2.index = v3.index
This is using the join mechanism to generate the combinations.
This method finds all combinations regardless of ordering (so 1, 2, 3 is the same as 2, 3, 1). Also, this ignores duplicates, so it cannot find (1, 2, 2) if 2 is repeated twice.
SELECT
CONCAT(CAST(n1 AS VARCHAR(10)),'|',CAST(n2 AS VARCHAR(10)),'|',CAST(n3 AS VARCHAR(10))) AS Combination,
COUNT(CONCAT(CAST(n1 AS VARCHAR(10)),'|',CAST(n2 AS VARCHAR(10)),'|',CAST(n3 AS VARCHAR(10)))) AS Occurrences
FROM
MyTable
GROUP BY
CONCAT(CAST(n1 AS VARCHAR(10)),'|',CAST(n2 AS VARCHAR(10)),'|',CAST(n3 AS VARCHAR(10)))
This creates a single column that represents the combination of the values within the 3 columns by concatenating the values. It will count the occurrences of each.
I am dealing with an issue and need some expert advice on to achieve the problem, my sql query generates output with two columns, 1st column displays id (for e.g. abc-123 in following table) and next column displays corresponding result to the id stored in db which is pass or fail.
I need to implement, when resolution is pass it should display success attempt, in following example, abc-123 failed 1st time however def-456 passed in next attempt thus success rate is 50%, now counter should reset and go to next row where there is pass thus it should show 100%, again when code hits pass counter resets then goes next and displays 33% bec there are two fail and one pass at the end, how it can be achieved in sql? (id and resolution are column names)
**date** **id resolution**
6/6/2012 abc-123 fail 50%
6/7/2012 abc-456 pass
6/8/2012 abc-789 pass 100%
6/9/2012 abc-799 fail 33%
6/10/2012 abc-800 fail
6/1/2012 abc-900 pass
Thanks
SELECT
*
FROM
table
INNER JOIN
(
SELECT
MIN(g.id) AS first_id,
MAX(g.id) AS last_id,
COUNT(*) AS group_size
FROM
table AS p
INNER JOIN
table AS g
ON g.id > COALESCE(
(SELECT MAX(id) FROM table WHERE id < p.id AND resolution = 'pass'),
''
)
AND g.id <= p.id
WHERE
p.resolution = 'pass'
GROUP BY
p.id
)
AS groups
ON table.id >= groups.first_id
AND table.id <= groups.last_id
There's more than one way to do it:
SELECT st.*,
#prev:=#counter + 1,
#counter:= CASE
WHEN st.resolution = 'pass'
THEN 0
ELSE #counter + 1
END c,
CASE WHEN #counter = 0
THEN CONCAT(FORMAT(100/#prev, 2), '%')
ELSE '-'
END res
FROM so_test st, (SELECT #counter:=0) sc
Here's proof of concept.