Mysql comparison between two datasets - mysql

I'm having troubles with mysql, I guess I'm missing some special functions to solve my problem.
I have a table, like this:
id - user_id - user_property
1 - 45 - 9986
2 - 45 - 9564
3 - 45 - 9225
4 - 45 - 9824
5 - 45 - 9711
6 - 83 - 9711
7 - 83 - 9924
8 - 83 - 9986
9 - 12 - 9933
10 - 12 - 9993
11 - 72 - 9189
12 - 72 - 9711
13 - 72 - 9225
14 - 72 - 9824
user_id+user_property is unique key
and I have a list of properties, like "9711","9225","9824". I'm trying to get a list of users having ALL those properties, in the most performant way possible. I've tried many ways, like doing 3 single queries and counting results like this
select count(distinct user_id) as tot from
( select user_id from mytable where user_property = 9711
union select user_id from mytable where user_property = 9225
union select user_id from mytable where user_property = 9824) as tmp
having tot = 3
another guess was to merge user properties per-user and searching wanted properties with the function FIND_IN_SET(element, set of elements obtained with GROUP_CONCAT(user_properties separator ','))
the problem is, mytable is really huge, I already need to select data from this and another table joined by user_id (and elaborate results another time after) and I'm guessing there is some better way to do that in terms of performances. any suggestions?
Thanks in advance

What about something like this?
SELECT COUNT(user_id) FROM (
SELECT user_id
FROM mytable
WHERE user_property IN (9711,9225,9824)
GROUP BY user_id
HAVING COUNT(*) >= 3
) users_with_all_properties

You can combine SUM with IN CLAUSE
SELECT COUNT(*)
FROM
(SELECT
user_id,SUM(user_property in (9711, 9225,9824)) sumprop
FROM mytable
GROUP BY user_id
Having sumprop = 3) userhasprop
| COUNT(*) |
| -------: |
| 2 |
db<>fiddle here

Related

select rows in mysql with latest date for each quiz_id repeated multiple times

I have a table where each quiz ID is repeated multiple times. there is a date in front of each quiz id in each row. I want to select entire row for each quiz ID where date is latest with user. The date format is mm/dd/YYYY.
Sample -
USER_ID Quiz_id Name Date Marks .. .. ..
1 2 poly 4/3/2020 27
1 2 poly 4/3/2019 98
1 4 moro 4/3/2020 09
2 5 cat 4/12/2015 87
2 4 moro 4/3/2009 56
2 6 PP 4/3/2011 76
3 2 poly 4/3/2020 12
3 2 poly 5/3/2020 09
3 7 dog 4/3/2011 23
I want result look like this:Result
USER_ID Quiz_id Name Date Marks .. .. ..
1 2 poly 4/3/2020 27
1 4 moro 4/3/2020 09
2 5 cat 4/12/2015 87
2 4 moro 4/3/2009 56
2 6 PP 4/3/2011 76
3 2 poly 5/3/2020 09
3 7 dog 4/3/2011 23
You can use rank function to get the desired result:
Demo
SELECT A.* FROM (
SELECT A.*, RANK() OVER(PARTITION BY USER_ID,QUIZ_ID, NAME ORDER BY DATE DESC) RN FROM
Table1 A ORDER BY USER_ID) A WHERE RN = 1 ORDER BY USER_ID, QUIZ_ID;
I don't have MySQL installed so you will need to test and report back. The general idea is to identify the row of interest using max and a group by (table t). As the Date column appears to be text column (MySQL uses the format YYYY-MM-DD for dates) you will need to convert it to a date with str_to_date() so you can use the max() aggregate function. Finally, join with the original table (here table t2 to do the date conversion), as only the aggregate column(s) and columns named in the group by are well defined (in table t1), i.e.:
select USER_ID, Quiz_id, Date, Marks from (
select USER_ID, Quiz_id, max(str_to_date(Date, '%m/%d/%Y')) as Date2 from quiz group by 1, 2
) as t natural join (
select *, str_to_date(Date, '%m/%d/%Y') Date2 from Quiz
) as t2;
I don't recall off-hand but Date might be reserved word, in which case you will need to quote the column name, or ideally rename said column to use a better name.
Also, the original table is not in 3rd normal form as Quiz_id depends on Name. Quiz_id, as implied, should be a foreign key to a lookup table that holds the Name.

configure query to bring rows which have more than 1 entries

How to get those entries which have more than 1 records?
If it doesn't make sense... let me explain:
From the below table I want to access the sum of the commission of all rows where type is joining and "they have more than 1 entry with same downmem_id".
I have this query but it doesn't consider more entries scenario...
$search = "SELECT sum(commission) as income FROM `$database`.`$memcom` where type='joining'";
Here's the table:
id mem_id commission downmem_id type time
2 1 3250 2 joining 2019-09-22 13:24:40
3 45 500 2 egbvegr new time
4 32 20 2 vnsjkdv other time
5 23 2222 2 vfdvfvf some other time
6 43 42 3 joining time
7 32 353 5 joining time
8 54 35 5 vsdvsdd time
Here's the expected result: it should be the sum of the id no 2, 7 only
ie. 3250+353=whatever.
It shouldn't include id no 6 because it has only 1 row with the same downmem_id.
Please help me to make this query.
Another approach is two levels of aggregation:
select sum(t.commission) income
from (select sum(case when type = 'joining' then commission end) as commission
from t
group by downmem_id
having count(*) > 1
) t;
The main advantage to this approach is that this more readily supports more complex conditions on the other members of each group -- such as at most one "joining" record or both "joining" records and no more than two "vnsjkdv" records.
Use EXISTS:
select sum(t.commission) income
from tablename t
where t.type = 'joining'
and exists (
select 1 from tablename
where id <> t.id and downmem_id = t.downmem_id
)
See the demo.
Results:
| income |
| ----- |
| 3603 |
You can use subquery that will find all downmem_id having more than one occurrence in the table.
SELECT Sum(commission) AS income
FROM tablename
WHERE type = 'joining'
AND downmem_id IN (SELECT downmem_id
FROM tablename t
GROUP BY downmem_id
HAVING Count(id) > 1);
DEMO

MySql query: what's the better way to get the number of unique pairs in a set?

There is a member_gorup table:
id member_id group_id
-- --------- --------
1 23 17
2 23 17
3 23 13
4 24 17
5 24 5
6 25 13
What needs to be found is how many unique(distinct) member_ids each group_id has. In this case it is:
17 - 2
13 - 2
5 - 1
I managed to achieve this by using the following query:
SELECT COUNT(`group_id`), `group_id` FROM
(SELECT `member_id`, `group_id`
FROM `member_groups`
GROUP BY `member_id`, `group_id`) AS `groups`
GROUP BY `group_id`
Is there a way to achieve the same result without using the nested query - perhaps with DISTINCT?
(optional question: if so is it faster?)
COUNT() has an optional DISTINCT syntax for exactly this kind of problem:
SELECT `group_id`, COUNT(DISTINCT `member_id`) AS count_members
FROM `member_groups`
GROUP BY `group_id`;

MySQL Join 2 columns from table y into rows of table x

Trying to wrap my mind around how to write this SQL query.
Table X has 3 Columns: Year, ID, Value and looks like so
Year | ID | Value
2013 101 10000
2014 101 11000
2015 101 12000
2013 102 7000
2014 102 8000
2015 102 9000
And table Y has 3 Columns: ID, Curr_Year_Val, Next_Year_Val and looks like this
ID | Curr_Year_Val | Next_Year_Val
101 13000 14000
102 6000 5000
I would like to write a select statement to join these two tables together, but maintain the layout of Table X, like so:
Year | ID | Value
2013 101 10000
2014 101 11000
2015 101 12000
Curr_Year_Val 101 13000
Next_Year_Val 101 14000
Is there a way to achieve this result? I've figured out how to just do a left join to add the columns from table y to table x, but would rather have the columns from table y unpivoted to the rows of table x. Thanks much in advance - this seems like it should be so easy, I've been googling for hours but I'm probably not using the proper terminology for what I'm trying to do in my searches.
Thanks!
Sounds like you should use union all:
select year, id, value from x
union all
select 'curr_year_val', id, curr_year_val from y
union all
select 'next_year_val', id, next_year_val from y
order by 2, 1
SQL Fiddle Demo
BTW, other databases would require you to have the same data types for all columns when using union. This works though with mysql.
Uee union
select year, id, value
from tableX
where id ='101'
union
select 'curr_year_val', id, curr_year_val
from tableY
where id ='101'
union
select 'next_year_val', id, next_year_val
from tableY
where id ='101'

MySQL Group by week num w/ multiple date column

I have a table with columns similar to below , but with about 30 date columns and 500+ records
id | forcast_date | actual_date
1 10/01/2013 12/01/2013
2 03/01/2013 06/01/2013
3 05/01/2013 05/01/2013
4 10/01/2013 09/01/2013
and what I need to do is get a query with output similar to
week_no | count_forcast | count_actual
1 4 6
2 5 7
3 2 1
etc
My query is
SELECT weekofyear(forcast_date) as week_num,
COUNT(forcast_date) AS count_forcast ,
COUNT(actual_date) AS count_actual
FROM
table
GROUP BY
week_num
but what I am getting is the forcast_date counts repeated in each column, i.e.
week_no | count_forcast | count_actual
1 4 4
2 5 5
3 2 2
Can any one please tell me the best way to formulate the query to get what I need??
Thanks
try:
SELECT weekofyear(forcast_date) AS week_forcast,
COUNT(forcast_date) AS count_forcast, t2.count_actual
FROM
t t1 LEFT JOIN (
SELECT weekofyear(actual_date) AS week_actual,
COUNT(forcast_date) AS count_actual
FROM t
GROUP BY weekOfYear(actual_date)
) AS t2 ON weekofyear(forcast_date)=week_actual
GROUP BY
weekofyear(forcast_date), t2.count_actual
sqlFiddle
You have to write about 30 (your date columns) left join, and the requirement is that your first date column shouldn'd have empty week (with a count of 0) or the joins will miss.
Try:
SELECT WeekInYear, ForecastCount, ActualCount
FROM ( SELECT A.WeekInYear, A.ForecastCount, B.ActualCount FROM (
SELECT weekofyear(forecast_date) as WeekInYear,
COUNT(forecast_date) as ForecastCount, 0 as ActualCount
FROM TableWeeks
GROUP BY weekofyear(forecast_date)
) A
INNER JOIN
( SELECT * FROM
(
SELECT weekofyear(forecast_date) as WeekInYear,
0 as ForecastCount, COUNT(actual_date) as ActualCount
FROM TableWeeks
GROUP BY weekofyear(actual_date)
) ActualTable ) B
ON A.WeekInYear = B.WeekInYear)
AllTable
GROUP BY WeekInYear;
Here's my Fiddle Demo
Just in case someone else comes along with the same question:
Instead of trying to use some amazing query, I ended up creating an array of date_columns_names and a loop in the program that was calling this query, and for each date_column_name, performing teh asme query. It is a bit slower, but it does work