Select by a limited number of distinct values - mysql

I am trying to chunkage selects by distinct values of one column. Like give me all rows for the first five distinct values. Or give me all rows for next five distinct values of one column.
I have a table like this VBS_DOCUMENT:
PK T_DOCUMENT
1 1
2 1
3 1
4 3
5 3
6 3
7 5
8 5
9 6
10 7
SELECT * FROM VBT_DOCUMENT
WHERE T_DOCUMENT IN (SELECT DISTINCT T_DOCUMENT FROM VBT_DOCUMENT LIMIT 2 OFFSET 2);
But then I get this error:
1235 - This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
PK T_DOCUMENT
7 5
8 5
9 6

Use your subquery in the FROM clause and join it with the base table:
SELECT t.*
FROM (
SELECT DISTINCT T_DOCUMENT
FROM VBT_DOCUMENT
ORDER BY T_DOCUMENT
LIMIT 2 OFFSET 2
) x
JOIN VBT_DOCUMENT t on t.T_DOCUMENT = x.T_DOCUMENT
Result:
| PK | T_DOCUMENT |
| --- | ---------- |
| 7 | 5 |
| 8 | 5 |
| 9 | 6 |
View on DB Fiddle
Note: When you use LIMIT you should also define a deterministic ORDER BY clause. Otherwise you might get an unexpected result depending on the execution plan.

WITH can be used in MySQL8.0 (,or MariaDB 10.2.1 )
WITH abc as (SELECT DISTINCT T_DOCUMENT FROM VBS_DOCUMENT LIMIT 2 OFFSET 2)
SELECT * FROM VBS_DOCUMENT WHERE T_DOCUMENT IN (select * from abc);
output:
# pk, t_document
'7', '5'
'8', '5'
'9', '6'

Related

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

Select where max Mysql

help please make sql select to database. There are such data.
My table is:
id news_id season seria date_update
---|------|---------|-----|--------------------
1 | 4 | 1 | 7 | 2017-04-14 16:38:10
2 | 4 | 1 | 7 | 2017-04-14 17:38:10
5 | 4 | 1 | 7 | 2017-04-14 16:38:10
3 | 4 | 1 | 7 | 2017-04-14 16:38:10
4 | 4 | 1 | 7 | 2017-04-14 16:38:10
6 | 4 | 1 | 7 | 2017-04-14 16:38:10
7 | 4 | 1 | 7 | 2017-04-14 16:38:10
8 | 1 | 1 | 25 | 2017-04-23 18:42:00
Need to get all cells grouped by max season and seria and date and sorted by date_update DESC.
In result i need next rows
id news_id season seria date_update
---|------|---------|-----|--------------------
8 | 1 | 1 | 25 | 2017-04-23 18:42:00
2 | 4 | 1 | 7 | 2017-04-14 17:38:10
Because this rows have highest season and seria and date_update per One news_id. I.e i need to select data wich have highest season and seria and date_update grouped by news_id and also sorted by date_update DESC
I tried so, but the data is not always correct, and it does not always for some reason cover all the cells that fit the condition.
SELECT serial.*
FROM serial as serial
INNER JOIN (SELECT id, MAX(season) AS maxseason, MAX(seria) AS maxseria FROM serial GROUP BY news_id) as one_serial
ON serial.id = one_serial.id
WHERE serial.season = one_serial.maxseason AND serial.seria = one_serial.maxseria
ORDER BY serial.date_update
Please, help. Thank.
The specification is unclear.
But we do know that the GROUP BY news_id clause is going collapse all of the rows with a common value of news_id into a single row. (Other databases would throw an error with this syntax; we can get MySQL to throw a similar error if we include ONLY_FULL_GROUP_BY in the sql_mode.)
My suggestion would be to remove the GROUP BY news_id clause from the end of the query.
But that's just a guess. It's not at all clear what you are trying to achieve.
EDIT
SELECT t.*
FROM (
SELECT r.news_id
, r.season
, r.seria
, MAX(r.date_update) AS max_date_update
FROM (
SELECT p.news_id
, p.season
, MAX(p.seria) AS max_seria
FROM (
SELECT n.news_id
, MAX(n.season) AS max_season
FROM serial n
GROUP BY n.news_id
) o
JOIN serial p
ON p.news_id = o.news_id
AND p.season = o.max_season
) q
JOIN serial r
ON r.news_id = q.news_id
AND r.season = q.season
AND r.seria = q.max_seria
) s
JOIN serial t
ON t.news_id = s.news_id
AND t.season = s.season
AND t.seria = s.seria
AND t.date_update = s.max_date_update
GROUP BY t.news_id
ORDER BY t.news_id
Or, an alternate approach making use of MySQL user-defined variables...
SELECT s.id
, s.season
, s.seria
, s.date_update
FROM (
SELECT IF(q.news_id = #p_news_id,0,1) AS is_max
, q.id
, #p_news_id := q.news_id AS news_id
, q.season
, q.seria
, q.date_update
FROM ( SELECT #p_news_id := NULL ) r
CROSS
JOIN serial q
ORDER
BY q.news_id DESC
, q.season DESC
, q.seria DESC
, q.date_update DESC
) s
WHERE s.is_max
ORDER BY s.news_id
The subquery selects the maximum season and the maximum seria per news_id. How many records exist for the news_id that match both the maximum season and the maximum seria we don't know. It can be, one or two or thousand or zero.
So with the join you get an unknown number of records per news_id. Then you group by news_id. This gets you one result row per news_id. How then can you select serial.*? * means all columns from a row, but which row,when there can be many for a news_id? MySQL usually picks values arbitrarily in this case (usually all from the same row, but even that is not guaranteed). So you end up with random rows which you order by date_update.
This doesn't make much sense. So the question is: what do you really want to achieve? Maybe my explanation suffices and you are able now to fix your query yourself.

How to fetch average rows per day?

I have a table
id | created
---------------
1 | 2014-07-01
2 | 2014-07-01
3 | 2014-07-01
4 | 2014-07-01
5 | 2014-07-02
6 | 2014-07-03
7 | 2014-07-04
8 | 2014-07-05
9 | 2014-07-05
10 | 2014-07-05
and I want to fetch the average of rows per day. So if it's
2014-07-01: 4 rows
2014-07-02: 1 row
2014-07-03: 1 row
2014-07-04: 1 row
2014-07-05: 3 rows
The average would be
sum 4 + 1 + 1 + 1 + 3 = 10 items
10 items / 5 days = 2 items/day
My SQL query is
SELECT AVG(COUNT(*))
FROM `mytable`
GROUP BY `created`
But I get this error
#1111 - Invalid use of group function
How do I have to modify my query in order to get a correct result?
You can do this without subquery also.It is much fatser.
SELECT COUNT(*)/COUNT(DISTINCT `created`) FROM `mytable`;
Here is the example link: http://sqlfiddle.com/#!2/2c284df/2
You need to put it in a subquery.
SELECT AVG(my_count) FROM (
SELECT COUNT(*) as my_count
FROM
your_table
GROUP BY created
) sq
see it working live in an sqlfiddle
Have a sub query to get the count per day, and then use that to get the average:-
SELECT AVG(day_count)
FROM
(
SELECT COUNT(*) AS day_count
FROM `mytable`
GROUP BY `created`
) sub0

mysql limit with inner join and subquery

I have the following query:
SELECT saturday_combinations.index, v.val AS `row` , COUNT( * ) AS `count`
FROM saturday_combinations
INNER JOIN (
SELECT ONE AS val
FROM saturday_combinations
WHERE ONE IS NOT NULL
UNION
SELECT TWO AS val
FROM saturday_combinations
WHERE TWO IS NOT NULL
UNION
SELECT THREE AS val
FROM saturday_combinations
WHERE THREE IS NOT NULL
UNION
SELECT FOUR AS val
FROM saturday_combinations
WHERE FOUR IS NOT NULL
UNION
SELECT FIVE AS val
FROM saturday_combinations
WHERE FIVE IS NOT NULL
UNION
SELECT SIX AS val
FROM saturday_combinations
WHERE SIX IS NOT NULL
UNION
SELECT SEVEN AS val
FROM saturday_combinations
WHERE SEVEN IS NOT NULL
) v ON v.val = saturday_combinations.ONE
OR v.val = saturday_combinations.TWO
OR v.val = saturday_combinations.THREE
OR v.val = saturday_combinations.FOUR
OR v.val = saturday_combinations.FIVE
OR v.val = saturday_combinations.SIX
OR v.val = saturday_combinations.SEVEN
GROUP BY v.val
The purpose of the query is to provide a count of the different values contained in the columns ONE,TWO,THREE,FOUR,FIVE,SIX and SEVEN in the table saturday_combinations. However I want to put a desc limit 4 so that it only performs the count based on the last 4 rows (last four maximum indexes). But I am not getting it to work with the union. Adding order and limit at the very end only limits from the final select, rather than get the last 4 rows and calculate the distribution on them. Any tips?
The table schema is as follows:
index | ONE|TWO|THREE|FOUR|FIVE|SIX|SEVEN
1 1 3 7 10 11 12 13
2 3 4 5 30 31 22 23
3 1 2 3 4 5 6 7
4 1 2 3 4 5 6 7
5 1 2 3 4 5 6 7
6 1 2 3 4 5 6 7
7 1 2 3 4 5 6 7
8 1 2 3 4 5 6 7
9 1 2 3 4 5 6 7
10 1 2 3 4 5 6 7
Index is auto-increment and ONE-SEVEN has integer values.
There are about 3000 rows in the table and I want to count occurences for each value based on the last n rows.
Ideal result for the last n rows where n = last 3 rows should be
Numbers|Count
1 3
2 3
3 3
4 3
5 3
6 3
7 3
If I increase n to include last 6 rows their count should increase. If I could last 10 rows the count should increase and other numbers should appear with their count.
Here is a link to a sample of the real table.
http://sqlfiddle.com/#!2/d035b
If answer to my comment is yes then, you could try the following. When you need to add limit, order by to union selects you need to wrap union queries with brackets ().
SQLFIDDLE DEMO
Code:
(SELECT ONE AS val
FROM saturday_combinations
WHERE ONE IS NOT NULL
order by ONE desc limit 4)
UNION
(SELECT TWO AS val
FROM saturday_combinations
WHERE TWO IS NOT NULL
order by TWO desc limit 4)
UNION
(SELECT THREE AS val
FROM saturday_combinations
WHERE THREE IS NOT NULL
order by THREE desc limit 4)
If answer to my comment is no, then please clarify.
Here is the code based on your sample date:
select distinct x.one as uniqunumbers,
count(x.one) as counts
from(
sELECT DISTINCT 'one'
AS col1, one FROM sat_comb
UNION ALL
SELECT DISTINCT 'two'
AS col1, two FROM sat_comb
UNION ALL
SELECT DISTINCT 'three'
AS col1, three FROM sat_comb
) as x
group by x.one;
UNIQUNUMBERS COUNTS
1 1
3 2
4 1
5 1
7 1
EDIT as per OP has clarified and updated the question.
Quoted: "However I want to limit it so that it first takes the last n rows and then does the count on the values in those n rows. This means, if I have 3 columns with 3000 rows and 35 integers randomly appearing in these 3000 rows it should count how many times each integer appears."
SQLFIDDLE DEMO2
Query:
select x.one as uniqunumbers,
count(x.one) as counts
from(
(sELECT DISTINCT 'one'
AS col1, one FROM sat_comb
order by id desc limit 4)
UNION ALL
(SELECT DISTINCT 'two'
AS col1, two FROM sat_comb
order by id desc limit 4)
UNION ALL
(SELECT DISTINCT 'three'
AS col1, three FROM sat_comb
order by id desc limit 4)
UNION ALL
(SELECT DISTINCT 'four'
AS col1, four FROM sat_comb
order by id desc limit 4)
UNION ALL
(SELECT DISTINCT 'five'
AS col1, five FROM sat_comb
order by id desc limit 4)
UNION ALL
(SELECT DISTINCT 'six'
AS col1, six FROM sat_comb
order by id desc limit 4)
UNION ALL
(SELECT DISTINCT 'seven'
AS col1, seven FROM sat_comb
order by id desc limit 4)
) as x
group by x.one;
Output:
UNIQUNUMBERS COUNTS
2 4
3 3
4 3
5 4
6 4
8 3
9 4
20 3
Maybe I am missing something in your request but based on your desired result, why not just unpivot the data and perform the count.
select value, count(*) Total
from
(
select 'one' col, one value
from saturday_combinations
union all
select 'two' col, two value
from saturday_combinations
union all
select 'three' col, three value
from saturday_combinations
union all
select 'four' col, four value
from saturday_combinations
union all
select 'five' col, five value
from saturday_combinations
union all
select 'six' col, six value
from saturday_combinations
union all
select 'seven' col, seven value
from saturday_combinations
) src
group by value
See SQL Fiddle with Demo
The result of your sample is:
| VALUE | TOTAL |
-----------------
| 1 | 1 |
| 3 | 2 |
| 4 | 1 |
| 5 | 1 |
| 7 | 1 |
| 10 | 1 |
| 11 | 1 |
| 12 | 1 |
| 13 | 1 |
| 22 | 1 |
| 23 | 1 |
| 30 | 1 |
| 31 | 1 |
Edit #1: Based on your update this might be want you what:
select value, count(*)
from
(
select col, value
from
(
select 'one' col, one value
from saturday_combinations
order by one
limit 3
) one
union all
select col, value
from
(
select 'two' col, two value
from saturday_combinations
order by two desc
limit 3
) two
union all
select col, value
from
(
select 'three' col, three value
from saturday_combinations
order by three
limit 3
) three
union all
select col, value
from
(
select 'four' col, four value
from saturday_combinations
order by four
limit 3
) four
union all
select col, value
from
(
select 'five' col, five value
from saturday_combinations
order by five
limit 3
) five
union all
select col, value
from
(
select 'six' col, six value
from saturday_combinations
order by six
limit 3
) six
union all
select col, value
from
(
select 'seven' col, seven value
from saturday_combinations
order by seven
limit 3
) seven
) src
group by value
See SQL Fiddle with Demo
Result:
| VALUE | COUNT(*) |
--------------------
| 1 | 3 |
| 2 | 1 |
| 3 | 4 |
| 4 | 4 |
| 5 | 3 |
| 6 | 3 |
| 7 | 3 |
In case you want to look at the final solution:
http://sqlfiddle.com/#!2/867a6/13
Thanks to #bonCodigo for all the help

Select all rows with multiple occurrences

Ok, I have a single MySQL table with the name 'test' and 3 columns.
ID | playername | lastloginip
-----------------------------
1 | user 1 | 1
2 | user 2 | 2
3 | user 3 | 3
4 | user 4 | 4
5 | user 5 | 5
6 | user 6 | 1
7 | user 7 | 1
8 | user 8 | 2
Now, I would like to select ALL the rows where the lastloginip is found multiple times in the table, and then give the count of those rows.
In this case, it should return the number 5
as user 1, 2, 6, 7 and 8 have a lastloginip that is found multiple times.
I already tried using
SELECT COUNT(*)
FROM (
SELECT *
FROM test
GROUP BY lastloginip
HAVING COUNT(*) > 1
) t
But that gave back the number 2 instead of 5.
I am not sure how to set up this query correctly. Most of my findings on the internet keep showing only 2 rows or giving the number 2 instead of 5.
First COUNT(), then SUM():
SELECT SUM(occurences)
FROM
(
SELECT COUNT(*) AS occurences
FROM test
GROUP BY lastloginip
HAVING COUNT(*)>1
) t
Try this query.
SELECT SUM(loginip)
FROM(
SELECT
lastloginip,
COUNT(lastloginip)
as loginip
FROM test
GROUP BY lastloginip
HAVING COUNT(ID)>1
)t
You can fetch the sum of occurrences using the above code and if you want to view the records with multiple occurences, refer to the query below-
Select *
from test
where lastloginip in (
select *
from
(select lastloginip
from test
group by lastloginip
having count(lastloginip)>1
)
as a)