SQL group by where values are same within group - mysql

So, I have a table with which individuals (person_id) have multiple lines (up to 4) and values for a column (value_column) can either = 0 or 1
I'd like to write a piece of code that returns a row for each person_id in which their value for value_column is only 0 or only 1 (even though they may have 3 or 4 rows each)
It's probably an easy line of code, but for someone with less SQL experience, it seems nearly impossible!
EDIT: here is a quick sample of lines:
person_id value_column
A 0
A 1
A 0
B 0
B 0
B 0
B 0
C 1
C 1
C 1
And I would expect the line of code to return the folowing:
person_id value_column
B 0
C 1

You can try something like this probably
select distinct * from table1
where person_id in
( select person_id
from table1
group by person_id
having count(distinct value_column) <= 1
)
Inner query, will return only those person_id for which there is only one value_column present and that's the same thing getting done by count(distinct value_column) <= 1 and then outer query just selects everything for those person_id.

select * from myTable where person_id not in
(select a.person_id from myTable a, myTable b
where a.person_id = b.person_id
and a.value_column <> b.value_column)
Get persons with different values and then get those who are not in this first query.
Or quicker and nicer :
select person_id, min(value_column)
from myTable
group by person_id
having min(value_column)=max(value_column)

Related

MySQL - Finding how much duplicates are inside the same table given

Considering I have the following two sets of rows (same type) in a WHERE clause:
A B
1 1
2 2
3 4
I need to find how many A is in B
For example, for the given table above, it would be 66% since 2 out of 3 numbers are in B
Another example:
A B
1 1
2 2
3 4
5
3
Would give 100% since all of the numbers in A are in B
Here is what I tried myself: (Doesn't work on all test cases..)
DROP PROCEDURE IF EXISTS getProductsByDate;
DELIMITER //
CREATE PROCEDURE getProductsByDate (IN d_given date)
BEGIN
SELECT
Product,
COUNT(*) AS 'total Number',
(SELECT
(SELECT COUNT(DISTINCT Part) FROM products WHERE Product=B.Product) - COUNT(*)
FROM
products AS b2
WHERE
b2.SOP < B.SOP AND b2.Part != B.Part) AS 'New Parts',
CONCAT(round((SELECT
(SELECT COUNT(DISTINCT Part) FROM products WHERE Product=B.Product) - COUNT(*)
FROM
products AS b2
WHERE
b2.SOP < B.SOP AND b2.Part != B.Part)/count(DISTINCT part)*100, 0), '%') as 'Share New'
FROM
products AS B
WHERE
b.SOP < d_given
GROUP BY Product;
END//
DELIMITER ;
CALL getProductsByDate (date("2018-01-01"));
Thanks.
Naming your tables TA and TB respectively you could try something like this (test made on MSSQL and Mysql at moment)
SELECT ROUND(SUM(PERC) ,4)AS PERC_TOT
FROM (
SELECT DISTINCT TA.ID , 1.00/ (SELECT COUNT(DISTINCT ID) FROM TA) AS PERC
FROM TA
WHERE EXISTS ( SELECT DISTINCT ID FROM TB WHERE TB.ID=TA.ID)
) C;
Output with your first sample data set:
PERC_TOT
0,6667
Output with your second sample data set:
PERC_TOT
1,0000
Update (I made the original for two tables, as I was thinking at solution). This is for one single table (is almost the same than the former query): (I used ID1 for column A and ID2 for column B)
SELECT ROUND(SUM(PERC) ,4)AS PERC_TOT
FROM (
SELECT DISTINCT TA.ID1 , 1.00/ (SELECT COUNT(DISTINCT ID1) FROM TA) AS PERC
FROM TA
WHERE EXISTS ( SELECT DISTINCT ID2 FROM TA AS TB WHERE TB.ID2=TA.ID1)
) C;

How can I return rows where only duplicates within a certain column exist?

Suppose that my database looks something like the following:
First Entry | Second Entry | Third Entry
0 0 0
0 1 2
2 1 0
2 0 1
3 0 0
I am trying to return the subset of this table where an element in the FirstEntry column repeats at least once. So in this case it would return all but the final row. How can I go about this? I've tried using things such as count() but have only managed to achieve grouping instead of returning the actual rows I am curious in (in particular, I care aboout Second and Third entry but only when First entry has repeated at least once).
Using IN()
select * from your_table
where FirstEntry in
(
select FirstEntry
from your_table
group by FirstEntry
having count(*) > 1
)
or using a JOIN
select t1.*
from your_table t1
join
(
select FirstEntry
from your_table
group by FirstEntry
having count(*) > 1
) t2 on t1.FirstEntry = t2.FirstEntry
Try this:
select *
from my_table
where First_Entry IN(SELECT First_Entry From my_table
group by First_entry having count(*) > 1)

repeat result multiple times in mysql

I have a table having id and no field, what I really want is the result raw will be repeated no filed times, if the no field is 2 then that raw must be repeated twice in result.
this is my sample table structure:
id no
1 3
2 2
3 1
now I need to get a result like:
1 3
1 3
1 3
2 2
2 2
3 1
I tried to write mysql query to get the result like above, but failed.
You need a table of numbers to accomplish this. For just three values, this is easy:
select t.id, t.no
from t join
(select 1 as n union all select 2 union all select 3
) n
on t.no <= n.no;
This query must do what you want to achieve:
select t.id, t.no from test t cross join test y where t.id>=y.id
not completely solve your problem, but this one can help
set #i=0;
select
test_table.*
from
test_table
join
(select
#i:=#i+1 as i
from
any_table_with_number_of_rows_greater_than_max_no_of_test_table
where
#i < (select max(no) from test_table)) tmp on no >= i
order by
id desc
EDIT :
This is on SQL Server. I checked online and see that CTEs work on MySQL too. Just couldn't get them to work on SQLFiddle
Try this, remove unwanted columns
create table #temp (id int, no int)
insert into #temp values (1, 2),(2, 3),(3, 5)
select * from #temp
;with cte as
(
select id, no, no-1 nom from #temp
union all
select c.id, c.no, c.nom-1 from cte c inner join #temp t on t.id = c.id and c.nom < t.no and c.nom > 0
)
select * from cte order by 1
drop table #temp

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).

Sql query to get products associated with flags

I have a flags_products table like below (many to many relationship between flags and products),
flags_products :
flag_id -- product_id
1 -- 1
2 -- 1
3 -- 1
1 -- 2
2 -- 2
4 -- 2
What is the SQL query to get rows that have both the flags (flag_id's 1 and 2) associated with a product (product_id)? Obviously:
SELECT *
FROM flags_products
WHERE flag_id = 1
AND flag_id = 2
GROUP BY product_id;
...doesn't work, and gives an empty set. So what would be the correct query?
The exact approach that you should take will depend on your larger requirements. Here's one possible solution:
SELECT
product_id
FROM
Some_Table
WHERE
flag_id IN (1, 2)
GROUP BY
product_id
HAVING
COUNT(*) = 2
This works as long as you can't have duplicates. If a product can be flagged twice with the same flag_id then you would need:
SELECT
product_id
FROM
Some_Table
WHERE
flag_id IN (1, 2)
GROUP BY
product_id
HAVING
COUNT(DISTINCT flag_id) = 2
In both of these cases you'll need the GROUP BY to match your column list. MySQL doesn't require that in order for the query to run (a flaw IMO, but I'll save that argument for another time ), but the results won't be determinable for the columns not in the GROUP BY. You can also use the above to queries as subqueries which you can then join to another table or tables.
If you know that it will always be exactly two flags for which you're looking then you can use EXISTS:
SELECT
T1.product_id
FROM
Some_Table T1
WHERE
EXISTS (
SELECT *
FROM
Some_Table T2
WHERE
T2.product_id = T1.product_id AND
T2.flag_id = 1
) AND
EXISTS (
SELECT *
FROM
Some_Table T2
WHERE
T2.product_id = T1.product_id AND
T2.flag_id = 2
)
Performance may not be very good though.
You can use INTERSECT operator.
SELECT product_id
FROM t
WHERE flag_id = 1
INTERSECT
SELECT product_id
FROM t
WHERE flag_id = 2
This query will return all product_id which have both flag_id = 1 or 2