Multiple select, subgroups - mysql

I have a little advise from SQL. I need to select a two groups (WHERE) in two columns. Some working 'like':
SELECT COUNT(WHERE Draw=1) as D1, COUNT(WHERE Draw=2) as D2 FROM SampleData
Exemple data table:
SampleData
--------------------
Id | Draw | Element
--------------------
1 | 1 | 13
2 | 1 | 15
3 | 1 | 22
4 | 1 | 36
5 | 1 | 45
6 | 2 | 11
7 | 2 | 15
8 | 2 | 22
And output like this:
Output:
--------
D1 | D2
--------
5 | 3

You can use CASE expression for this:
SELECT
COUNT(CASE WHEN Draw=1 THEN 1 END) as D1,
COUNT(CASE WHEN Draw=2 THEN 1 END) as D2
FROM SampleData
mysql also supports If() so you could also do something like the following. Just keep in mind this isn't portable to other RDBMS's but the CASE version is:
SELECT
SUM(IF(Draw=1, 1, 0)) as D1,
SUM(IF(Draw=2, 1, 0)) as D2
FROM SampleData;
Also... mysql supports math on Boolean expressions so you could get really terse here:
SELECT SUM(Draw=1) as D1, Sum(Draw=2) as D2 FROM SampleData;
Again though this is not portable to other RDBMSs like the CASE expression is.

Maybe something like this? (untested)
SELECT COUNT(*) as D1 FROM SampleData WHERE Draw = 1 UNION SELECT COUNT(*) as D2 FROM SampleData WHERE Draw = 2

Related

return only runner(s) greater than zero for every year

Currently compiled a dataset with the following data.
Year, runner, total.
Grouped into the following result.
I would like to programatically determine if a runner (can be 1 - 30 or less) represented as 1-4 above, has a total value > 0 in every year with a max of two runners. Return the runner(s) or nothing if no matches found.
Therefore either a value greater than 0 in every year, or with a max of two runners added together has a combined total of greater than 0 in every year between 2015 and 2019. If the query returns more than one result I could write this to a table and return the max value afterwards.
I could write something like add runner 1+2, 1+3, 1+4 etc for every possible combination to a table but this seems overkill. I cannot sum up all years and then use the max funtion for the biggest value as this doesn't mean all the years is greater than zero. Is there a easier/better way of achieving this?
Can this be done with a MySQL query or another language that is worth learning for this kind of data analysis?
Would like to filter unwanted scenarios where either RUN1, or RUN2 has all negative values but still satisfies the requirement of RUN1 + run2 is greater than zero for all years.
Currenly working on where RUN1 is greater or equal to three years where the value is greater than zero but open to suggestions.
Demo Unclear Expected Result
I think this is close.
note baseData is a Common table expression just mocking up sample data
it can be omitted and the next lines adjusted to reference your tablename.
I'm not exactly sure what output you're after
how to handle the conditions such as my "2" which when combined with another runner will cause multiple users to be "valid"
If it's Runner 1+ Runner 2 value > 0 or Runner 1 > 0 or Runner 2 > 0...
if the former need more test data staged to sample edge cases.
Cases I'm not sure how to handle
If a runner by themselves fulfills the requirement their existence means all combinations of "2" runners involving that runner would also meet the requirement
if two runners together meet the requirement of value > 0 for all years... then the inverse pair would as well (1,2), (2,1) I think I can eliminate this by ensuring matches only occur when Runner1<=Runner2 (Commented out below)
.
with BaseData as (
SELECT 1 Runner, 2015 YR, -8.29 value UNION ALL
SELECT 1 Runner, 2016 YR, -8.29 value UNION ALL
SELECT 1 Runner, 2017 YR, -8.29 value UNION ALL
SELECT 1 Runner, 2018 YR, -8.29 value UNION ALL
SELECT 1 Runner, 2019 YR, -8.29 value UNION ALL
SELECT 2 Runner, 2015 YR, 1 value UNION ALL
SELECT 2 Runner, 2016 YR, 1 value UNION ALL
SELECT 2 Runner, 2017 YR, 1 value UNION ALL
SELECT 2 Runner, 2018 YR, 1 value UNION ALL
SELECT 2 Runner, 2019 YR, 1 value UNION ALL
SELECT 3 Runner, 2015 YR, 1 value UNION ALL
SELECT 3 Runner, 2016 YR, -1 value UNION ALL
SELECT 3 Runner, 2017 YR, -1 value UNION ALL
SELECT 3 Runner, 2018 YR, -1 value UNION ALL
SELECT 3 Runner, 2019 YR, -1 value UNION ALL
SELECT 4 Runner, 2015 YR, -1 value UNION ALL
SELECT 4 Runner, 2016 YR, 1 value UNION ALL
SELECT 4 Runner, 2017 YR, 1 value UNION ALL
SELECT 4 Runner, 2018 YR, 1 value UNION ALL
SELECT 4 Runner, 2019 YR, 1 value),
CTE as (
SELECT A.Runner RUN1, B.Runner RUN2,
sum(case when A.value>0 and A.Runner = B.Runner then 1 else 0 end) as isValidYear,
count(A.YR) TotalYears,
sum(case when A.Runner=B.Runner then A.value else 0 end) as TotalValue,
/*not sure if this should be (A.Value > 0 or B.Value > 0) OR (A.Value+B.Value>0)*/
sum(case when A.runner<> B.Runner and (A.Value>0 OR B.Value > 0) then 1 end) as isValid2PersonYR
FROM BaseData A
CROSS JOIN BaseData B
WHERE A.YR=B.YR
/* AND A.Runner<=B.Runner*/
GROUP BY A.Runner, B.Runner
Order by A.Runner)
SELECT RUN1, Run2, sum(isValidYear) SumIsvalidYear, Sum(isValid2PersonYR) SumIsvalid2PersonYr, TotalYears
FROM CTE
GROUP BY Run1, Run2, TotalYears
HAVING SumisValidYear = Totalyears OR SumisValid2personYR=TotalYears
ORDER BY SumIsValid2PersonYr, Run1
Giving us:
Note the 1st line the runner 1 and 2 is the same, there is no 2nd person, so they did it on their own.
+------+------+----------------+---------------------+------------+
| RUN1 | Run2 | SumIsvalidYear | SumIsvalid2PersonYr | TotalYears |
+------+------+----------------+---------------------+------------+
| 2 | 2 | 5 | | 5 |
| 1 | 2 | 0 | 5 | 5 |
| 2 | 1 | 0 | 5 | 5 |
| 2 | 3 | 0 | 5 | 5 |
| 2 | 4 | 0 | 5 | 5 |
| 3 | 2 | 0 | 5 | 5 |
| 3 | 4 | 0 | 5 | 5 |
| 4 | 2 | 0 | 5 | 5 |
| 4 | 3 | 0 | 5 | 5 |
+------+------+----------------+---------------------+------------+
Yes the combination 4,2 and 2,4 are the same, and we could eliminate them as needed: I think this gets you close but I need to understand how to handle situation for runner 2 or if we have a situation where multiple runners would be returned as they "complete" a runner's series, how do you want those?
We could use this result set to exclude all occurrences of a 2 except that of RUN1: 2 matching Run2:2 which would give us just 3,4 and 4,3 which do infect complete each other.
You can see this is "close" but not understanding how you want to handle these situations I stopped here.
If we include the commented out line in the SQL we get:
+------+------+----------------+---------------------+------------+
| RUN1 | Run2 | SumIsvalidYear | SumIsvalid2PersonYr | TotalYears |
+------+------+----------------+---------------------+------------+
| 2 | 2 | 5 | | 5 |
| 1 | 2 | 0 | 5 | 5 |
| 2 | 3 | 0 | 5 | 5 |
| 2 | 4 | 0 | 5 | 5 |
| 3 | 4 | 0 | 5 | 5 |
+------+------+----------------+---------------------+------------+
and these results make sense to me. the questions asked at the top of this response apply.
should (1,2),(2,3),(2,4) be in the results? (they only exist because (2) is complete by itself)
should results just be (2,2) since we had a runner meet the condition?
should results be (2,2) and (3,4) since you want to see all that met the conditions?

Displaying records where the same value have been mentioned more than 3 times in SQL

How can I find a value that has been mentioned several times in a row.
ID |1_Jan|3_Jan|4_Jan|4_Jan|
12 | 2 | 3 | 2 | 4 |
31 | 3 | 4 | 3 | 1 |
25 | 1 | 1 | 1 | 1 |
26 | 3 | 3 | 3 | 3 |
In the case of this table, I want to get out ID 25 and 26 because here the values 1 and 3 have been used 3 or more times in a record.
I was also wondering how can I for example only get out ID 25 even if 26 also has 3 or more?
You can select the rows with all equal column values, and then order by common column value:
with cte as (select t.id, t.1_Jan r, (t.1_Jan = t.2_Jan) and (t.2_Jan = t.3_Jan) and (t.3_Jan = t.4_Jan) val from test_table t)
select c.id from cte c where c.val = 1 order by c.r limit 1;
Output:
id
25
See demo.
This answers the original version of the question.
One way is to unpivot and aggregate:
select id, val, count(*)
from ((select id, 1_jan as val from t) union all
(select id, 2_jan as val from t) union all
(select id, 3_jan as val from t) union all
(select id, 4_jan as val from t)
) t
group by id, val
having count(*) >= 3;

SQL query to get count of unique combinations in the table - MySql

I have a mapping table like below
c1 c2
-- --
1 1
1 1
1 2
1 3
1 3
2 1
2 2
2 3
3 1
and so on. The table has a separate id column (not showed here).
Here is my query so far :
SELECT `c1`, `c2`, COUNT(*) AS `count_of_uniques` FROM `map_table`
GROUP BY `c1`, `c2`
I have also tried with distinct query like this.
SELECT `c1`, `c2`, COUNT(DISTINCT `c1`, `c2`) AS `count_of_uniques` FROM `map_table`
The expected result is
c1 c2 count_of_uniques
-- -- ----------------
1 1 2
1 2 2
1 3 3
2 2 1
2 3 1
My current query shows the correct output when the combination is made of two similar numbers, but when we have something like 1-2 and 2-1, the query does not have the correct output.
distinct shows still fewer results.
Any help is greatly appreciated. Thank you.
I think you might be after something that deals with combinations rather than permutations i.e 1, 2 and 2, 1 should be treated as the same combination. If this is the case you can use a case expression to make sure that c1 is always the lower of the two values, and c2 is the higher of the two. This will group similar pairs together (so 2, 1 will first be reversed to become 1, 2 then grouped with all similar results):
SELECT c1, c2, COUNT(*) AS `count_of_uniques`
FROM ( SELECT CASE WHEN c1 > c2 THEN c2 ELSE c1 END AS c1,
CASE WHEN c1 > c2 THEN c1 ELSE c2 END AS c2
FROM map_table) AS t
GROUP BY c1, c2
Output
c1 c2 count_of_uniques
-------------------------------
1 1 2
1 2 2
1 3 3
2 2 1
2 3 1
Example on DB Fiddle
You can use the functions least() and greatest() to get the pairs on which you will group:
select
least(c1, c2) c1,
greatest(c1, c2) c2,
count(*) count_of_uniques
from map_table
group by
least(c1, c2),
greatest(c1, c2)
See the demo.
Results:
| c1 | c2 | count_of_uniques |
| --- | --- | ---------------- |
| 1 | 1 | 2 |
| 1 | 2 | 2 |
| 1 | 3 | 3 |
| 2 | 2 | 1 |
| 2 | 3 | 1 |
Select C1,C2,Count(*) from map_table Group By C1,C2 Order by C1

Loop unique insert into temporary table MySql stored procedure

I have two tables that look like this:
Table 1
Type 1 | Type 2 | Type 3 | ...
1 | 3 | 0 | ...
Table 2
Type 1 | Type 2 | Type 3 | ...
3 | 2 | 1 | ...
I would like to combine them into a temporary table like this:
Temporary Table
UID | Type | Table
1 | Type 1 | 1
2 | Type 2 | 1
3 | Type 2 | 1
4 | Type 2 | 1
7 | Type 1 | 2
8 | Type 1 | 2
9 | Type 1 | 2
10 | Type 2 | 2
11 | Type 2 | 2
Essentially, the numbers in tables 1 and 2 are totals and I want to break them out into individual rows in this temporary table.
I started going down the path of selecting from both tables and storing the values into temporary variables. I was then going to loop through every single variable and insert into the temporary table. But I have about 15 columns per table and there has got to be an easier way of doing this. I just don't know what it is.
Does anyone have any insight on this? My knowledge is incredibly limited on MySql stored procedures.
Not sure of an easy way to do this. One option would be to have a numbers table. Heres a quick approach to getting 1-10 in a common-table-expression (change as needed).
Then you could join to each table and each type, using union all for each subset. Here is a condensed version:
with numbers as (select 1 n union all select 2 union all
select 3 union all select 4 union all select 5 union all
select 6 union all select 7 union all select 8 union all
select 9 union all select 10)
select 'type1' as type, '1' as tab
from numbers n join table1 t on n.n <= t.type1
union all
select 'type2' as type, '1' as tab
from numbers n join table1 t on n.n <= t.type2
union all
select 'type1' as type, '2' as tab
from numbers n join table2 t on n.n <= t.type1
union all
select 'type2' as type, '2' as tab
from numbers n join table2 t on n.n <= t.type2
Demo Fiddle

How to set mysql datediff result into row list

I would like to ask you guys about something about DateDiff in MySQL.
for example, here is some code of mysql datediff.
select datediff('2015-10-11', '2015-10-15') as Diffdate
and the result would be
| DiffDate |
------------
| 4 |
------------
so, my question is, how do I make the Diffdate result into some kind of row numbers from 1 to the result of diffDate?
this is the result that I want.
| DiffDate |
------------
| 1 |
| 2 |
| 3 |
| 4 |
------------
thank you in advance
The trick is first to select numbers from 1 to 31:
select n.x from
(Select 1 x
union select 2 x
union select 3 x
...
union select 31 x) n ,
(select datediff('2015-10-15', '2015-10-11') as Diffdate) d
where n.x <= d.Diffdate
I the example above should datediff('2015-10-11', '2015-10-15') is -4: that's why i have exchanged the args order.