Select row based on identifier and value from another row - mysql

I have a mySQL dataset that looks like this:
ID PARENT_ID VALUE
1 100 This comment should be approved
2 100 Y
3 101 Another approved comment
4 101 Y
5 102 This comment is not approved
6 102 N
I need to construct an SQL query to select the rows that have a matching parent_id and corresponding value of Y (but ignore the rows with single letters as a value in the result) to result in:
ID PARENT_ID VALUE
1 100 This comment should be approved
3 101 Another approved comment
My idea is to use GROUP BY to combine the columns, but I can't work out how to select based on the Y/N values.
There is possibly a solution here How do I select a row based on a priority value in another row? but I don't think it is asking quite the same question.
Any ideas?

Although you can express this as an aggregation, you can express this using exists:
select d.*
from dataset d
where d.value <> 'Y' and
exists (select 1
from dataset d2
where d2.parent_id = d.parent_id and d2.value = 'Y'
);
This version is probably more efficient.

First, if you possibly can, change your table schema. Your table is storing two kinds of data in the same field (yes no flags and comments). This breaks normality and will haunt you later.
But if its not your table to change, you will need to self join. Try this.
SELECT a.id, a.parent_Id, a.value
FROM table a inner join table b
ON a.parent_id =b.parent_id
WHERE a.value <> 'Y' and b.value ='Y'

Related

SQL Distinct cannot working if having count clause

I am having trouble with my sql query (Select distinct didnt work).
My sql is :
select distinct
count(T2.Column1)
from Table1 t2
where T2.Column1='2017-05-210'
The actual Column 1 data only have 3 data,
But the output is 12
Nb :
- Column1 data is having 1 to Many situation with Column2,
Here are the actual data:
Column 1 Column 2
1 A
1 B
1 C
1 D
2 A
2 B
2 C
2 D
3 A
3 B
3 C
3 D
Can anyone help me?
Really appreciate for your attention.
Thanks!
You want to count distinct values, so do count(distinct ):
select count(distinct T2.Column1)
from Table1 t2
where T2.Column1='2017-05-210'
(However, your sample data and the query's data/columns do not match.)
Your sample data, result and query do not match.
However, what your query does is:
Find all records with a column1 = '2017-05-210'.
Count all of these records where column1 is not null (which is true for all these records, as column1 = '2017-05-210').
This results in one number (one row, one column). But you additionally say with DISTINCT that you want to remove any duplicates from your result rows. With one row only there can be no duplicates, so the function is superfluous here.
So think about what you want to count really. You count distinct values (i.e. count ignoring duplicates) with COUNT(DISTINCT column), but COUNT(DISTINCT column1) would return 1 of course, because you are only looking for one value which is '2017-05-210' (or zero in case there is no record matching this value).

Select where field in values (if field in value not exist return as null)

I want to select a data in where clause. Say that I have $values: 1,2,3,4
And then I want to select rows from table in that values.
SELECT `date` from another_table where id in (1,2,3,4)
If another_table only have rows with id 1,2 and 3 only, it mean that the id of 4 is not exist. I want the id of 4 is still selected with return of null or nol or NEVER.
another_table
-------+-------------+
| id | date |
----------------------
1 Yesterday
2 Today
3 Tomorow
5 Today
The expected result would be
-------+-------------+
| id | date |
----------------------
1 Yesterday
2 Today
3 Tomorow
4 Never
How to do this?
Thanks in advance
As one option use an inline view as a rowsource, and perform an outer join operation. We can use an IF expression to check whether a matching row was returned from another_table, we know the id value will not be NULL if there was a matching row, the join predicate (in the ON clause) guarantees us that.)
As an example:
SELECT n.id
, IF(a.id IS NULL,'Never',a.date) AS `date`
FROM ( SELECT 1 AS id
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
) n
LEFT
JOIN another_table a
ON a.id = n.id
ORDER BY n.id
The inline view query gets executed, and the results are materialized into a temporary table (MySQL calls it a derived table). When the outer query runs, n is effectively a table containing 4 rows.
Obviously, if the list of id values has to change, you'd need to change the view definition. The SQL text for the inline view can be generated dynamically from an array in a programming language from an array.
For a large number of values, the inline view becomes unwieldy, and you'd get better performance from a table, rather than the view.

mysql select records where value occurs

I hope you can help me with this one. I've been looking for ways to set up a MySQL query that selects rows based on the number of times a certain value occurs, but have had no luck so far. I'm pretty sure i need to use count(*) somewhere, but i can only found how to count all values or all distinct values, instead of counting all occurences.
I have a table as such:
info setid
-- --
A 1
B 1
C 2
D 1
E 2
F 3
G 1
H 3
What i need is a query that will select all the lines where a setid occurs a certain number (x) of times.
So using x=2 should give me
C 2
E 2
F 3
H 3
because both setIds 2 and 3 each occur two times. Using x=1 or x = 3 should not give any results, and choosing x=4 should give me
A 1
B 1
D 1
G 1
Because only setid 1 occurs 4 times.
I hope you guys can help me. At this point i've been looking for the answer for so long that i'm not even sure this can be done in MySQL anymore. :)
select * from mytable
where setid in (
select setid from mytable
group by setid
having count(*) = 2
)
you can specify the # of times a setid needs to occur in the table in the having count(*) part of the subquery
Consider the following statement that uses an uncorrelated subquery:
SELECT ... FROM t1 WHERE t1.a IN (SELECT b FROM t2);
The optimizer rewrites the statement to a correlated subquery:
SELECT ... FROM t1 WHERE EXISTS (SELECT 1 FROM t2 WHERE t2.b = t1.a);
If the inner and outer queries return M and N rows, respectively, the execution time becomes on the order of O(M×N), rather than O(M+N) as it would be for an uncorrelated subquery.
But this time the subquery in Fuzzy Tree's solution is complety superfluous:
SELECT
set_id,
GROUP_CONCAT(info ORDER BY info) infos
COUNT(*) total
FROM
tablename
GROUP_BY set_id
HAVING COUNT(*) = 2

Adding one extra row to the result of MySQL select query

I have a MySQL table like this
id Name count
1 ABC 1
2 CDF 3
3 FGH 4
using simply select query I get the values as
1 ABC 1
2 CDF 3
3 FGH 4
How I can get the result like this
1 ABC 1
2 CDF 3
3 FGH 4
4 NULL 0
You can see Last row. When Records are finished an extra row in this format
last_id+1, Null ,0 should be added. You can see above. Even I have no such row in my original table. There may be N rows not fixed 3,4
The answer is very simple
select (select max(id) from mytable)+1 as id, NULL as Name, 0 as count union all select id,Name,count from mytable;
This looks a little messy but it should work.
SELECT a.id, b.name, coalesce(b.`count`) as `count`
FROM
(
SELECT 1 as ID
UNION
SELECT 2 as ID
UNION
SELECT 3 as ID
UNION
SELECT 4 as ID
) a LEFT JOIN table1 b
ON a.id = b.id
WHERE a.ID IN (1,2,3,4)
UPDATE 1
You could simply generate a table that have 1 column preferably with name (ID) that has records maybe up 10,000 or more. Then you could simply join it with your table that has the original record. For Example, assuming that you have a table named DummyRecord with 1 column and has 10,000 rows on it
SELECT a.id, b.name, coalesce(b.`count`) as `count`
FROM DummyRecord a LEFT JOIN table1 b
ON a.id = b.id
WHERE a.ID >= 1 AND
a.ID <= 4
that's it. Or if you want to have from 10 to 100, then you could use this condition
...
WHERE a.ID >= 10 AND
a.ID <= 100
To clarify this is how one can append an extra row to the result set
select * from table union select 123 as id,'abc' as name
results
id | name
------------
*** | ***
*** | ***
123 | abc
Simply use mysql ROLLUP.
SELECT * FROM your_table
GROUP BY Name WITH ROLLUP;
select
x.id,
t.name,
ifnull(t.count, 0) as count
from
(SELECT 1 AS id
-- Part of the query below, you will need to generate dynamically,
-- just as you would otherwise need to generate 'in (1,2,3,4)'
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5
) x
LEFT JOIN YourTable t
ON t.id = x.id
If the id does not exist in the table you're selecting from, you'll need to LEFT JOIN against a list of every id you want returned - this way, it will return the null values for ones that don't exist and the true values for those that do.
I would suggest creating a numbers table that is a single-columned table filled with numbers:
CREATE TABLE `numbers` (
id int(11) unsigned NOT NULL
);
And then inserting a large amount of numbers, starting at 1 and going up to what you think the highest id you'll ever see plus a thousand or so. Maybe go from 1 to 1000000 to be on the safe side. Regardless, you just need to make sure it's more-than-high enough to cover any possible id you'll run into.
After that, your query can look like:
SELECT n.id, a.*
FROM
`numbers` n
LEFT JOIN table t
ON t.id = n.id
WHERE n.id IN (1,2,3,4);
This solution will allow for a dynamically growing list of ids without the need for a sub-query with a list of unions; though, the other solutions provided will equally work for a small known list too (and could also be dynamically generated).

MySQL query returning no rows

I have two MySQL tables. What I am trying to do is to export the information where Value 1 is 1 less than Value 2 AND where ID_1 does not have its Value 1 and Value 2 equal.
Note:
Fields Value 1 and 2 are just integers.
Each distinct ID_A has the same Value_2
If there are two Value_1s that are one less than Value_2, look to Value_3 and select one that is higher
The reason why I have two tables here is because I am going to output information from both tables
We can write a script for this, but I need to do this in a single command for bonus points (which my instructor declared is possible)... I haven't even started a script for this, as I don't really know how to do that...
tableA looks like this:
ID_1 ID_2
A A
A B
B A
B B
C A
C B
C C
tableB looks like this:
ID_1 ID_2 Value_1 Value_2 Value_3
A A 2 3 NULL
A B 3 3 NULL
B A 4 5 NULL
B B 7 5 NULL
C A 7 8 98
C B 3 8 NULL
C C 7 8 56
The query should return this:
ID_1 ID_2
B A
C A
Here is what I have so far... And it keeps returning no hits, which is making me confused. I believe it is the AND clause after the first WHERE statement where I need to fix
SELECT CONCAT(...)
INTO OUTFILE '/tmp/outfile.tab'
FIELDS TERMINATED BY '\t'
ESCAPED BY ''
FROM tableA
INNER
JOIN tableB
ON tableA.ID_1 = tableB.ID_1
AND tableA.ID_2 = tableB.ID_2
WHERE tableB.Value_1 - 1 = tableB.Value_2
AND tableA.ID_1 !=
( SELECT DISTINCT
ID_1
FROM tableB
WHERE ID_1 = tableA.ID_1
AND Value_1 = Value_2
)
;
One final note: we issue all commands through putty, in which we can access MySQL
To be honest, I still don't understand exactly what you're trying to do, but I can explain why your query is returning no rows.
Look at this clause:
AND tableA.ID_1 !=
( SELECT DISTINCT
ID_1
FROM tableB
WHERE ID_1 = tableA.ID_1
AND Value_1 = Value_2
)
The subquery will necessarily always return either tableA.ID_1 or NULL. (Do you see why?) So the comparison is never "true"; it's always either "false" (because tableA.ID_1 != tableA.ID_1 is necessarily "false") or "null/indeterminate" (because tableA.ID_1 != NULL is "null/indeterminate"). Therefore, this clause filters out all results from your query — regardless of what the rest of your query might say.
I am not 100% sure of the question, but if I get it right, the first row of tableB (Line 25 in http://imgur.com/a/r3Qy5#1) should NOT be selected, because ID_1=A has Value_1=3 in the second row (Line 26 in http://imgur.com/a/r3Qy5#1), which is the same as Value_1 of the first row (Line 25 in http://imgur.com/a/r3Qy5#1).
So you could start with something like
SELECT .... FROM
tableA NATURAL JOIN tableB
WHERE Value_1=Value_2-1
AND Value_2 NOT IN (SELECT tb.Value_1 from tableB AS tb WHERE tb.ID_1=tableB.ID_1)
which fullfills requirements #1 and #2. For requirement #3 (if there are two rows for an ID_1, chose the one with the highest Value_3), we need to sort that on Value_3 and wrap it in a superquery for grouping:
SELECT .... FROM (
SELECT * FROM
tableA NATURAL JOIN tableB
WHERE Value_1=Value_2-1
AND Value_2 NOT IN (SELECT tb.Value_1 from tableB AS tb WHERE tb.ID_1=tableB.ID_1)
ORDER BY Value_3 DESC
) AS innerview
GROUP BY Value_1,Value_2
which gives the correct answer for the test data in your example.
You'll FIRST have to apply a test for your "Value_3" criteria grouped by the respective "ID_1" classification and Value1, value2. By applying the WHERE clause here, you are getting your final set of records INCLUSIVE of what WOULD be the highest value 3 entry in its result set. Now, that gets joined again to tableB AGAIN, but matching the qualifying entries. Since the COALESCE() will change any NULL value to 0 in the first result set, the JOIN clause must also match that. As in the case for the "A" and "B" groups where no Value_3 was applied, yet in the "C" group, it WILL have a valid value and pre-result in the entry with the max value of 98. That said, when re-joined back to instance "tb2" for TableB a second time will get the proper ID_2 of "A" for that set.
select
MaxQualified.ID_1,
tb2.ID_2,
MaxQualified.Value_1,
MaxQualified.Value_2,
tb2.Value_3
from
( select
tb.ID_1,
tb.Value_1,
tb.Value_2,
MAX( COALESCE( tb.Value_3, 0 ) ) as HighestVal3
from
TableB tb
where
tb.Value_1 +1 = tb.Value_2
group by
tb.ID_1,
tb.Value_1,
tb.Value_2 ) MaxQualified
JOIN TableB tb2
on MaxQualified.ID_1 = tb2.ID_1
AND MaxQualified.Value_1 = tb2.Value_1
AND MaxQualified.Value_2 = tb2.Value_2
AND MaxQualified.HighestVal3 = COALESCE( tb2.Value_3, 0 )
Now, that being said, and this is homework, this COULD fail or give multiple answers if you had multiple ID1, Value1, Value2, Value3 entries. It would return all "ID2" instances of the exact same common criteria. You would have to do even one more level nested to remove that level of distinction.
Your answer should ALSO return "A", "A", 2, 3