including limit to select top n rows within a group by clause - mysql

I have the following 2 tables:
create table1
(
SENDER int,
RECEIVER int,
TIME time,
TYPE char(1)
);
create table2
(
ID int,
Y int,
CONTACT int,
DATE time
);
I am executing the following join query:
SELECT B.ID, A.RECEIVER AS Z, A.SENDER AS CONTACT, A.TYPE, A.TIME
FROM table1 A
JOIN table2 B ON A.RECEIVER = B.CONTACT
WHERE A.TYPE = 'A'
AND A.TIME < B.DATE
How do I modify the query to return only the top 40 results for each (ID,CONTACT) pair using GROUP BY?
I can order the data using the field table2.DATE

since i wanted top 40 results for each ID, i made ID,autoId as a primary key, here autoId is an autoincrement key. so after executing the following query:
SELECT B.ID, A.RECEIVER AS Z, A.SENDER AS CONTACT, A.TYPE, A.TIME
FROM table1 A
JOIN table2 B ON A.RECEIVER = B.CONTACT
WHERE A.TYPE = 'A'
AND A.TIME < B.DATE
i get results such that, the autoId initializes to 1 for each ID
for eg:
ID CONTACT autoId
1 2 1
1 3 2
1 11 3
1 34 4
2 5 1
2 33 2
2 56 3
since autoId is autoincrement, there is already an index on it. after this table is created, i can easily delete the results where autoId is greater than 40. and this while process runs really fast!

Related

Use previous row result when current row is null

I have a table that has daily records of transactions and some rows are missing data which will make plotting a daily graph inconsistent.
I want a query to use the last row result when the current one is null so that it can look something like this:
The structure of my table looks like this:
I have tried working on this query to select the previous row and update the current row if it is null but is not dynamic.
SELECT BALANCE
FROM tbl_batch_balances_null
WHERE id =
(select min(id)
from tbl_batch_balances_null where id < '2' and balance is not null)
Schema (MySQL v5.7)
DROP TABLE IF EXISTS alfie;
CREATE TABLE alfie
(id INT AUTO_INCREMENT PRIMARY KEY,
store CHAR(1) NULL,
product INT NULL
);
INSERT INTO alfie VALUES
( 7,'a',3),
( 8,null,null),
( 9,null,null),
(10,null,null),
(11,'a',1),
(12,'a',1),
(13,'a',1),
(14,null,null),
(15,null,null),
(16,'b',2),
(17,null,null),
(18,null,null),
(19,null,null);
Query #1
SELECT a.id,
COALESCE(a.store, c.store) store,
COALESCE(a.product,c.product) product
FROM alfie a
LEFT
JOIN
( SELECT x.*,
MAX(y.id) y_id
FROM alfie x
JOIN alfie y
ON y.id < x.id
AND y.store IS NOT NULL
WHERE x.store IS NULL
GROUP
BY x.id
) b
ON b.id = a.id
LEFT
JOIN alfie c
ON c.id = b.y_id
ORDER
BY id;
id
store
product
7
a
3
8
a
3
9
a
3
10
a
3
11
a
1
12
a
1
13
a
1
14
a
1
15
a
1
16
b
2
17
b
2
18
b
2
19
b
2
View on DB Fiddle

JOIN, GROUP BY, SUM Issue Mysql

Assuming I have this table
tableA
ID value
1 5
1 5
3 10
2 4
2 2
1 2
tableB
ID Name
1 apple
2 carrot
3 banana
If the expected max value of apple is 10, carrot is 5, and banana is 15 the output table would be
table output
ID Name value
1 apple 12
2 carrot 6
what SQL statement I need to solve this?
what I have done so far:
SELECT a,ID, b.name , sum(a.valueSUM) AS value FROM tableA a
INNER JOIN tableB b
ON a.id = b.id
GROUP BY id
what options i need on the WHERE clause to pull this off?
The inner subquery groups them normally and then the main query is what deals with limiting the results.
SELECT * FROM
(select
b.id,
b.name as name,
SUM(a.value) as the_sum
from tableA a inner join tableB b
on a.Id = b.id
group by b.name, b.id
) y
where (name = 'apple' and the_sum >= 10) OR
(name = 'banana' and the_sum >= 15) OR
(name = 'carrot' and the_sum >= 5)
It seems your sample data has changed, please try this. I thought the ID doesnt have to follow tableA/tableB's id and the id is auto-generated as per the results.
Would be nice if you have another table that sets the threshold per name
Assuming threshold can be specified in tableB (makes sense):
SELECT a.ID, b.name, sum(a.value) AS value
FROM tableA a
INNER JOIN tableB b
ON a.id = b.id
GROUP BY a.ID, b.name, b.Threshold
HAVING sum(a.value) > b.Threshold;
Demo: http://rextester.com/ICOQF10295
SELECT TableB.id, TableB.Name, MAX(TableA.value) AS Value
FROM TableA INNER JOIN TableB ON
TableA.id = TableB.id
GROUP BY TableB.id, TableB.Name
Instead of SUM, use MAX aggregate function
This works in SQL Server
--Existing tables
create table #tableA (ID int, value int)
create table #tableB (ID int, Name varchar(30))
insert into #tableA
select 1 , 5 union all
select 1 , 5 union all
select 3 , 10 union all
select 2 , 4 union all
select 2 , 2 union all
select 1 , 2
insert into #tableB
select 1 , 'apple' union all
select 2 , 'carrot' union all
select 3 , 'banana'
--Create new temporary table #tableC
create table #tableC (ID int, MAXvalue int)
insert into #tableC
select 1 , 10 union all
select 2 , 5 union all
select 3 , 15
select c.ID,b.Name, a.value from #tableC c
inner join #tableB b on b.ID = c.ID
inner join (
select ID,SUM(value) as value from #tableA
group by ID
) a on a.ID = c.ID
where a.value >= c.MAXvalue
drop table #tableA
drop table #tableB
drop table #tableC

Select date closest to date in another table

I have two tables, one having a entry date, and the other with an effective date. What I need to do is select the row were the entrydate is closest to the effective date. The only resource I can find is row_number() which does not seem to work in MySQL.
data
Table A Table B
id effdate id Aid entrydate
1 2015-10-19 1 1 2015-12-17
2 1 2015-12-18
3 1 2015-12-20
What I am trying to do is select
id effdate entrydate
1 2015-10-19 2015-12-17
So far I have tried using min() on entrydate, but it will just time out.
SELECT a.id, a.effdate, b.entrydate
FROM tableA a
JOIN tableB b on a.id = b.Aid
SELECT a.id, a.effdate, b.entrydate
FROM tableA a
JOIN tableB b on a.id = b.Aid
ORDER BY DATEDIFF(entrydate, effdate) ASC
-- you might want to order here by additional fields to break the ties
LIMIT 1;
If entry date is always greater than the effective date you can use the following
select a.id, a.effdate, b.entrydate from aa a, bb b
where a.id = b.aid
and b.entrydate = (select Min(bi.entrydate)
from bb bi
where bi.id = a.id
);

Using a nested query to get details of two tables

TABLE 1 TABLE 2
id name mob id course mark
1 joe 0000 1 English 77
2 john 0000 2 maths 89
I need to show the name of the person from table 1 who has the MAX(grade) in table 2 using a nested query.
SELECT t1.name
FROM t1
WHERE t1.id = t2.id = (
SELECT id
FROM t2
WHERE mark =
(
SELECT MAX(mark)
FROM t2
)
);
Well, this satisfies the brief ;-):
SELECT a.*
FROM table_a a
JOIN (SELECT * FROM table_b) b
ON b.id = a.id
ORDER
BY mark DESC
LIMIT 1;

SQL: finding differences between rows

I want to count how many times each user has rows within '5' of eachother.
For example, Don - 501 and Don - 504 should be counted, while Don - 501 and Don - 1600 should not be counted.
Start:
Name value
_________ ______________
Don 1235
Don 6012
Don 6014
Don 6300
James 9000
James 9502
James 9600
Sarah 1110
Sarah 1111
Sarah 1112
Sarah 1500
Becca 0500
Becca 0508
Becca 0709
Finish:
Name difference_5
__________ _____________
Don 1
James 0
Sarah 2
Becca 0
Use the ABS() function, in conjunction with a self-join in a subquery:
So, something like:
SELECT name, COUNT(*) / 2 AS difference_5
FROM (
SELECT a.name name, ABS(a.value - b.value)
FROM tbl a JOIN tbl b USING(name)
WHERE ABS(a.value - b.value) BETWEEN 1 AND 5
) AS t GROUP BY name
edited as per Andreas' comment.
Assuming that each name -> value pair is unique, this will get you the count of times the value is within 5 per name:
SELECT a.name,
COUNT(b.name) / 2 AS difference_5
FROM tbl a
LEFT JOIN tbl b ON a.name = b.name AND
a.value <> b.value AND
ABS(a.value - b.value) <= 5
GROUP BY a.name
As you'll notice, we also have to exclude the pairs that are equal to themselves.
But if you wanted to count the number of times each name's values came within 5 of any value in the table, you can use:
SELECT a.name,
COUNT(b.name) / 2 AS difference_5
FROM tbl a
LEFT JOIN tbl b ON NOT (a.name = b.name AND a.value = b.value) AND
ABS(a.value - b.value) <= 5
GROUP BY a.name
See the SQLFiddle Demo for both solutions.
Because the OP also wants de zero counts, we'll need a self- left join. Extra logic is needed if one person has two exactly the same values, these should also be counted only once.
WITH cnts AS (
WITH pair AS (
SELECT t1.zname,t1.zvalue
FROM ztable t1
JOIN ztable t2
ON t1.zname = t2.zname
WHERE ( t1.zvalue < t2.zvalue
AND t1.zvalue >= t2.zvalue - 5 )
OR (t1.zvalue = t2.zvalue AND t1.ctid < t2.ctid)
)
SELECT DISTINCT zname
, COUNT(*) AS znumber
FROM pair
GROUP BY zname
)
, names AS (
SELECT distinct zname AS zname
FROM ztable
GROUP BY zname
)
SELECT n.zname
, COALESCE(c.znumber,0) AS znumber
FROM names n
LEFT JOIN cnts c ON n.zname = c.zname
;
RESULT:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 14
zname | znumber
-------+---------
Sarah | 3
Don | 1
Becca | 0
James | 0
(4 rows)
NOTE: sorry for the CTE, I had not seen th mysql tag,I just liked the problem ;-)
SELECT
A.Name,
SUM(CASE WHEN (A.Value < B.Value) AND (A.Value >= B.Value - 5) THEN 1 ELSE 0 END) Difference_5
FROM
tbl A INNER JOIN
tbl B USING(Name)
GROUP BY
A.Name