Searching a Record from Middle of the Table - mysql

I am facing issue in finding the data from MySql table.
Table A:
+-------+-------------+------+-----+-------------------+-------+
| ID | Table_b_fk |Value | age | name | score |
+-------+-------------+------+-----+-------------------+-------+
| 01 | 01 | 255 | 21 | Tom | 65 |
| 02 | 02 | 36 | 20 | Peter | 95 |
| 03 | 03 | 25 | 22 | John | 65 |
| 04 | 04 | 36 | 20 | Bond | 95 |<<----First
| 05 | 05 | 258 | 22 | Smith | 65 |
| 06 | 06 | 420 | 20 | Robert | 95 |
| 07 | 07 | 258 | 22 | Nisarg Patel | 65 |
| 08 | 08 | 16 | 21 | Peter | 95 |
| 09 | 09 | 25 | 23 | J0k | 65 |
| 10 | 10 | 36 | 22 | Prodigitalson | 95 |
| 11 | 11 | 205 | 22 | Silver | 65 |<<----Next
| 12 | 12 | 37 | 20 | Json | 95 |
| 13 | 13 | 285 | 23 | Villa | 65 |
| 14 | 14 | 36 | 22 | Parker | 95 |
+-------+-------------+------+-----+-------------------+-------+
Table B:
+-------+-------------+------+-----+-------------------+-------+
| ID | Result | M1 | M2 | name | score |
+-------+-------------+------+-----+-------------------+-------+
| 01 | Pass | 30 | 26 | Tom | 65 |
| 02 | Pass | 30 | 20 | Peter | 95 |
| 03 | Pass | 25 | 60 | John | 65 |
| 04 | Pass | 100 | 100 | Bond | 95 |<<----First
| 05 | Pass | 55 | 65 | Smith | 65 |
| 06 | Pass | 80 | 95 | Robert | 95 |
| 07 | Pass | 65 | 75 | Nisarg Patel | 65 |
| 08 | Pass | 56 | 71 | Peter | 95 |
| 09 | Pass | 90 | 96 | J0k | 65 |
| 10 | Pass | 96 | 96 | Prodigitalson | 95 |
| 11 | Pass | 100 | 100 | Silver | 65 |<<----Next
| 12 | Pass | 47 | 92 | Json | 95 |
| 13 | Pass | 82 | 73 | Villa | 65 |
| 14 | Pass | 86 | 72 | Parker | 95 |
+-------+-------------+------+-----+-------------------+-------+
I am joining TableA & TableB, where in TableA Table_b_fk is foreign key to TableB.
I am finding the record which matches the TableB column M1 & M2 = 100.
My Scenario: 1
I know the first occurrence of the match record ID : 04 in TableA. I want to do a search to find the next record with M1 & M2 = 100. (Record Id-11) But the search should not start from 01. It should start from the last found record Id. That is from O4 the search should start to find the next occurrence of the record.
My Try:
I tried to find using Limit but it didn't help me to find. Can some one help me in this?
Edit: 1
My Scenario: 2
In my second case my TableB has repeated Data and the ID was foreign in TableA. How can I fins the record. ? with the matching ID/M1/M2 values: I found a solution for that. I just want to find the Current Record FOREIGN KEY and Check for the next occurrence of the record in the same table and I can get the next record rite?
In this case my TableB record are not as same as TableA records. In other words my TableA records will point to tableA. Many-to-one. Is this rite?
Edit: 2
Thanks for all your efforts and knowledge I found a solution for scenario:2 check it:
CREATE TABLE TableB (
ID Int,
Result VARCHAR(20),
M1 INT,
M2 INT,
name VARCHAR(20),
Score INT);
INSERT INTO TableB VALUES
( 11 , 'Pass' , 30 , 26 , 'Tom' , 65 ),
( 13 , 'Pass' , 30 , 20 , 'Peter' , 95 ),
( 80 , 'Pass' , 25 , 60 , 'John' , 65 ),
( 81 , 'Pass' , 100 , 100 , 'Bond' , 95 ),
( 90 , 'Pass' , 55 , 65 , 'Smith' , 65 ),
( 96 , 'Pass' , 80 , 95 , 'Robert' , 95 ),
( 97 , 'Pass' , 65 , 75 , 'Nisarg Patel' , 65 ),
( 98 , 'Pass' , 56 , 71 , 'Peter' , 95 ),
( 99 , 'Pass' , 90 , 96 , 'J0k' , 65 ),
( 100 , 'Pass' , 96 , 96 , 'Prodigitalson' , 95 ),
( 101 , 'Pass' , 10 , 10 , 'Silver' , 65 ),
( 103 , 'Pass' , 47 , 92 , 'Json' , 95 ),
( 201 , 'Pass' , 82 , 73 , 'Villa' , 65 ),
( 222 , 'Pass' , 86 , 72 , 'Parker' , 95 )
;
CREATE TABLE TableA
(`ID` int, `Table_b_fk` int, `Value` int, `age` int, `name` varchar(13), `score` int)
;
INSERT INTO TableA
(`ID`, `Table_b_fk`, `Value`, `age`, `name`, `score`)
VALUES
(01, 11, 255, 21, 'Tom', 65),
(02, 81, 36, 20, 'Peter', 95),
(03, 80, 25, 22, 'John', 65),
(04, 97, 36, 20, 'Bond', 95),
(05, 81, 258, 22, 'Smith', 65),
(06, 06, 420, 20, 'Robert', 95),
(07, 81, 258, 22, 'Nisarg Patel', 65),
(08, 08, 16, 21, 'Peter', 95),
(09, 96, 25, 23, 'J0k', 65),
(10, 101, 36, 22, 'Prodigitalson', 95),
(11, 222, 205, 22, 'Silver', 65),
(12, 12, 37, 20, 'Json', 95),
(13, 201, 285, 23, 'Villa', 65),
(14, 101, 36, 22, 'Parker', 95)
;
Solution for that is:
SELECT a.id
FROM TableB b
INNER JOIN TableA a
ON a.Table_b_fk = b.id
WHERE M1 = 100 and M2 = 100 AND a.ID>4 limit 1
where the limit just limits the next record.. (answer is 5).
I case of Doctrine 2: Use the below Query code.
$qry = $this->manager()->createQueryBuilder()
->select(array('e', 's'))
->from('YOUR_DOMAIN', 'e')
->Join('e.table_b_k', 's')
->where("s.m1 = ?", $valueone)
->andwhere("s.m2 = ?", $valuetwo)
->andwhere("e.id > ?", $currentrecord)
->setMaxResult(1);
Note: YOUR_DOMAIN here is the TableA. TableA and TableB should be joined through the Mapping so we dont need to Join/Reference int he Query.. directly as TableB. The will be done by the second line Join in the above example. It is not tested as of now.

What about this give it a try
SELECT b.id AS next_id FROM tableb b LEFT JOIN tablea a
ON (b.id =a.Table_b_fk ) WHERE b.M1=100 AND b.M2 =100 AND b.id >4
ORDER BY b.id ASC LIMIT 1
It gives the next occurrence of *M1 =100 and M2 =100 *
See Fiddle Example it returns 11 the next occurrence *M1 =100 and M2 =100 *

If I understand your question correctly, I think you are looking for this:
SET #id:=4;
SELECT *
FROM TableA
WHERE Table_b_fk =
(SELECT MIN(ID)
FROM TableB
WHERE (M1,M2) = (SELECT M1, M2 FROM TableB WHERE ID=#id)
AND ID>#id)
This query will select the second row from TableA that has M1=100 and M2=100 in the second table.

try this
SELECT a.id
FROM TableB b
INNER JOIN TableA a
ON a.Table_b_fk = b.id
WHERE M1 = 100 and M2 = 100 AND b.ID>4
output:
ID
11
demo FIDLLE

I am finding the record which matches the TableB column M1 & M2 = 100.
Assuming you mean:
I am finding the tableA record which matches the TableB column M1 = 100 AND M2 = 100.
SELECT *
FROM table_a a
WHERE EXISTS (
SELECT *
FROM table_b b
WHERE b.id = a.tableb_fk
AND b.m1 = 100 AND b.m2 = 100
);
UPDATE: since the OP appears to want to suppress the first matching record from the result (I assume: the one with the lowest id), one could add an extra EXIST to the WHERE clause to suppress the first match:
SELECT *
FROM table_a a
WHERE EXISTS (
SELECT *
FROM table_b b
WHERE b.id = a.tableb_fk
AND b.m1 = 100 AND b.m2 = 100
AND EXISTS (
SELECT *
FROM table_b bb
WHERE bb.id < b.id
AND bb.m1 = 100 AND bb.m2 = 100
)
);

Related

Can i decide to have x null rows on a left join?

There is my issue :
I have two tables that i want to left join, and i want to display X rows depending on how many records there are in the second table.
Example :
TABLE_A TABLE_B
--------------------- -----------------------------------
| idA | yearA | | idB | idRefA | integ | lab |
|-------------------| |-------------------------------- |
| 1 | 2010 | | 20 | 2 | 54 | x |
| 2 | 2011 | | 20 | 2 | 50 | y |
| 3 | 2012 | | 20 | 2 | 28 | z |
| 4 | 2013 | | 21 | 3 | 18 | x |
| 5 | 2014 | | 21 | 3 | 22 | y |
--------------------- | 21 | 3 | 32 | z |
------------------------------------
There are my two tables.
This is my SQL query to join them :
SELECT * FROM TABLE_A
LEFT JOIN TABLE_B ON TABLE_A.ida = TABLE_B.idrefa
But i haven't the result that i expect, here is the result :
idA
yearA
idB
idRefA
integ
lab
1
2010
null
null
null
null
2
2011
20
2
28
z
2
2011
20
2
50
y
2
2011
20
2
54
x
3
2012
21
3
32
z
3
2012
21
3
22
y
3
2012
21
3
18
x
4
2013
null
null
null
null
5
2014
null
null
null
null
I am trying to have X rows on my LEFT JOIN (here X = 3). To complete the missing rows. Something like :
1 2010 19 1 0 x
1 2010 19 1 0 y
1 2010 19 1 0 z
...
Is it possible to do with a SELECT and LEFT JOIN ?
Here is my desired result :
ida
yearA
idb
idrefa
integ
lab
1
2010
null
null
null
null
1
2010
null
null
null
null
1
2010
null
null
null
null
2
2011
20
2
28
z
2
2011
20
2
50
y
2
2011
20
2
54
x
3
2012
21
3
32
z
3
2012
21
3
22
y
3
2012
21
3
18
x
Thanks for answer
Schemar and insert statements:
create table TABLE_A(idA int, yearA int);
insert into TABLE_A values( 1 , 2010 );
insert into TABLE_A values( 2 , 2011 );
insert into TABLE_A values( 3 , 2012 );
insert into TABLE_A values( 4 , 2013 );
insert into TABLE_A values( 5 , 2014 );
create table TABLE_B( idB int, idRefA int, integ int, lab varchar(10) );
insert into TABLE_B values( 20 , 2 , 54 , 'x' );
insert into TABLE_B values( 20 , 2 , 50 , 'y' );
insert into TABLE_B values( 20 , 2 , 28 , 'z' );
insert into TABLE_B values( 21 , 3 , 18 , 'x' );
insert into TABLE_B values( 21 , 3 , 22 , 'y' );
insert into TABLE_B values( 21 , 3 , 32 , 'z' );
Query#1 (for MySQL version older than 8.0):
SELECT min(ida)over() ida,min(yeara)over()yearA,idb,idrefa,integ,lab FROM TABLE_A
LEFT JOIN TABLE_B ON TABLE_A.ida = TABLE_B.idrefa
where TABLE_B.idrefa is null
union all
SELECT * FROM TABLE_A
LEFT JOIN TABLE_B ON TABLE_A.ida = TABLE_B.idrefa
where TABLE_B.idrefa is not null
Output:
ida
yearA
idb
idrefa
integ
lab
1
2010
null
null
null
null
1
2010
null
null
null
null
1
2010
null
null
null
null
2
2011
20
2
54
x
2
2011
20
2
50
y
2
2011
20
2
28
z
3
2012
21
3
18
x
3
2012
21
3
22
y
3
2012
21
3
32
z
Query#2 (for MySQL version 8.0 and above):
SELECT (case when idrefa is null then min(ida)over(partition by idrefa) else ida end)ida,(case when idrefa is null then min(yeara)over(partition by idrefa) else yearA end)yearA,idb,idrefa,integ,lab FROM TABLE_A
LEFT JOIN TABLE_B ON TABLE_A.ida = TABLE_B.idrefa
order by yearA
Output:
ida
yearA
idb
idrefa
integ
lab
1
2010
null
null
null
null
1
2010
null
null
null
null
1
2010
null
null
null
null
2
2011
20
2
28
z
2
2011
20
2
50
y
2
2011
20
2
54
x
3
2012
21
3
32
z
3
2012
21
3
22
y
3
2012
21
3
18
x
db<fiddle here

Get difference between two records

I got a User-Information table where every 24 hours a new record is added for each user. This record contains a user_id, a value (a counter) and the creation date.
TBL_EXAMPLE
ID | user_id | cnt_val | record_date
--------------------------------------------
1 | 10 | 46 | 2019-02-05 12:14:35
2 | 21 | 12 | 2019-02-05 12:14:35
3 | 32 | 453 | 2019-02-05 12:14:35
4 | 10 | 23 | 2019-02-06 16:11:21
5 | 21 | 34 | 2019-02-06 16:11:21
6 | 32 | 480 | 2019-02-06 16:11:21
7 | 10 | 31 | 2019-02-07 11:34:25
8 | 21 | 44 | 2019-02-07 11:34:25
9 | 32 | 489 | 2019-02-07 11:34:25
...
Expected Result:
User 10 Counter: 46 .. 31 --> Difference: 15
User 21 Counter: 12 .. 44 --> Difference: 32
User 32 Counter: 453.. 489 --> Difference: 36
I want to make a list of each difference for each specific user from the oldest to the newest data record in the table dynamically.
you could use inner join twice on table_exeple and a subquery for min and max date
select distinct t1.user_id, t1.cnt_va - t2.cnt_val
from (
select user_id , min(date) min_date, max(date) max_date
from TTBL_EXAMPLE
group by user_id
) tmm
inner join TTBL_EXAMPLE t2 ON t2.date = tmm.max_date
and t2.user_id = tmm.user_id
inner join TBL_EXAMPLE t1 ON t1.date = tmm.min_date
and t1.user_id = tmm.user_id

How can I club values in MySql

I have two columns coming from my sql query- month, value i.e. values are coming monthwise. My requirement is to club these months in the group of 3 months wise...and the values should come the average of these 3.
Ex.I have following data-
Month Values
Mar-14 50
Apr-14 51
May-14 52
Jun-14 53
Jul-14 54
Aug-14 55
Sep-14 56
Oct-14 57
Nov-14 58
Dec-14 59
Jan-15 60
Feb-15 61
Mar-15 62
Apr-15 63
May-15 64
Jun-15 65
Jul-15 66
Aug-15 67
Sep-15 68
Oct-15 69
Nov-15 70
Dec-15 71
Jan-16 72
Feb-16 73
Mar-16 74
Apr-16 75
May-16 76
Jun-16 77
Jul-16 78
Aug-16 79
Sep-16 80
Oct-16 81
Nov-16 82
Dec-16 83
Jan-17 84
Feb-17 85
Mar-17 86
How can I achieve following output in MySql-
3 Months Clubing Avg of Values
Mar-14 51
Jun-14 54
Sep-14 57
Dec-14 60
Mar-15 63
Jun-15 66
Sep-15 69
Dec-15 72
Mar-16 75
Jun-16 78
Sep-16 81
Thanks in Advance
A bit messy but you could use variables -assuming you have an incrementing id column (or soemthing you can order by)
drop table if exists t;
create table t(id int auto_increment primary key,Month varchar(10), Valus int);
insert into t (month,valus) values
('Mar-14', 50),
('Apr-14', 51),
('May-14', 52),
('Jun-14', 53),
('Jul-14', 54),
('Aug-14', 55),
('Sep-14', 56),
('Oct-14', 57),
('Nov-14', 58),
('Dec-14', 59);
select id,mth,rt
from
(
select id,month,valus,
#count:=#count+1 counter,
if(#count=1,#mth:=month,#mth:=#mth) mth,
if(#count=1,#block:=#block+1,#block:=#block) block,
if(#count<3,#sum:=#sum+valus,#sum:=(#sum+valus) / 3) rt,
if(#count=3,#count:=0,#count:=#count) creset,
if(#count=0,#sum:=0,#sum:=#sum) sumreset
from t
cross join (select #m ='',#count:=0,#sum:=0,#block:=0,#mth:='') s
order by id
)t
where counter = 3;
+----+--------+------+
| id | mth | rt |
+----+--------+------+
| 3 | Mar-14 | 51 |
| 6 | Jun-14 | 54 |
| 9 | Sep-14 | 57 |
+----+--------+------+
3 rows in set (0.03 sec)
Slightly less messy but using sql's avg function and using variables to fill down the first month in a 3 month block
select block,mth,avg(valus)
from
(
select id,month,valus,
#count:=#count+1 counter,
if(#count=1,#mth:=month,#mth:=#mth) mth,
if(#count=1,#block:=#block+1,#block:=#block) block,
if(#count=3,#count:=0,#count:=#count) creset
from t
cross join (select #block:=0,#count:=0,#mth:='') s
order by id
) t
group by block,mth
order by block,mth
+-------+--------+------------+
| block | mth | avg(valus) |
+-------+--------+------------+
| 1 | Mar-14 | 51.0000 |
| 2 | Jun-14 | 54.0000 |
| 3 | Sep-14 | 57.0000 |
| 4 | Dec-14 | 59.0000 |
+-------+--------+------------+
4 rows in set (0.05 sec)
Try this
create temporary table tab (month1 varchar(30), id int);
insert into tab (month1,id)
values('Mar-14' ,50),
('Apr-14' ,51),
('May-14' ,52),
('Jun-14' ,53),
('Jul-14' ,54),
('Aug-14' ,55),
('Sep-14' ,56),
('Oct-14' ,57),
('Nov-14' ,58),
('Dec-14' ,59),
('Jan-15' ,60),
('Feb-15' ,61),
('Mar-14' ,62);
set #row_number = 0;
select *
from tab where (#row_number := #row_number+1)%3= 1;
Result
month1 id
'Mar-14' '50'
'Jun-14' '53'
'Sep-14' '56'
'Dec-14' '59'
'Mar-14' '62'

Match column name to data in MYSQL

I have data like in table.
Item | 7/7/15 | 7/8/15 | 7/9/15
1 | 23 | 24 | 25
2 | 26 | 74 | 96
and
I have table which has,
Item | Date | Number
1 | 7/9/15 | 56
1 | 7/7/15 | 75
1 | 7/8/15 | 63
I want to find sum of Number from 7/7/15 to 7/8/15 from table 1 and sum of the number from second table.
My table should look like
Item | StartDate | EndDate | no. | TotalNumber
item 7/7/15 7/8/15 7/9/15
1 23 24 25
2 26 74 96
item date number
1 7/9/15 56
1 7/7/15 75
1 7/8/15 63
.
SELECT
i1.Item,
'7/7/15' AS "StartDate",
'7/8/15' AS "EndDate",
(SELECT SUM(`7/7/15`)+SUM(`7/8/15`) FROM table1 WHERE item=i1.item) AS no,
(SELECT SUM(number) FROM table2 WHERE item=i1.item) "TotalNumber"
FROM
table2 i2
RIGHT OUTER JOIN table1 i1 on i1.item=i2.item;
item startdate enddate no TotalNumber
1 7/7/15 7/8/15 47 194
1 7/7/15 7/8/15 47 194
1 7/7/15 7/8/15 47 194
2 7/7/15 7/8/15 100
.
It's working..

Still show the proper set of time even if there's no entry for that time

I have this query where it gets the average and group the values by 15 mins from 12 AM to 11:45 PM.
SELECT FROM_UNIXTIME(t_stamp/1000, '%m/%d/%Y %l:%i %p') as t_stamp,
ROUND(AVG(CASE WHEN id = '001' THEN value END),2) Value1,
ROUND(AVG(CASE WHEN id = '002' THEN value END),2) Value2,
ROUND(AVG(CASE WHEN id = '003' THEN value END),2) Value3
FROM table1
WHERE tagid IN ("001", "002", "003") and
date(from_unixtime(t_stamp/1000)) BETWEEN "2014-05-01" AND "2014-05-01"
GROUP BY DATE(from_unixtime(t_stamp/1000)), HOUR(from_unixtime(t_stamp/1000)), MINUTE(from_unixtime(t_stamp/1000)) DIV 15
The output looks like this
t_stamp | Value1 | Value2 | Value3
05/01/2014 12:00 AM | 199 | 99 | 100
05/01/2014 12:15 AM | 299 | 19 | 140
05/01/2014 12:30 AM | 399 | 59 | 106
05/01/2014 12:45 AM | 499 | 59 | 112
.
.
.
05/01/2014 11:00 PM | 149 | 199 | 100
05/01/2014 11:15 PM | 599 | 93 | 123
05/01/2014 11:30 PM | 129 | 56 | 150
05/01/2014 11:45 PM | 109 | 60 | 134
It works fine but I've noticed that sometimes if there's no entry for like the time 12:30 instead of showing
t_stamp | Value1 | Value2 | Value3
05/01/2014 12:00 AM | 199 | 99 | 100
05/01/2014 12:15 AM | 299 | 19 | 140
05/01/2014 12:30 AM | Null | Null | Null
05/01/2014 12:45 AM | 499 | 59 | 112
It will show the set of time like this:
t_stamp | Value1 | Value2 | Value3
05/01/2014 12:00 AM | 199 | 99 | 100
05/01/2014 12:15 AM | 299 | 19 | 140
05/01/2014 12:33 AM | 122 | 141 | 234
05/01/2014 12:45 AM | 499 | 59 | 112
What I would like to happen is when there's no time for that 15 min group it will still show the proper set of time and then just show null on the column values. The output I would like is like this:
t_stamp | Value1 | Value2 | Value3
05/01/2014 12:00 AM | 199 | 99 | 100
05/01/2014 12:15 AM | 299 | 19 | 140
05/01/2014 12:30 AM | Null | Null | Null
05/01/2014 12:45 AM | 499 | 59 | 112
How can I do this?
Thank You.
You need a table that's a source of cardinal numbers as a start for this. For the moment let's assume it exists, and it's called cardinal.
Then, you need to create a query (a virtual table) that will return rows with timestamps every fifteen minutes, starting with the earliest relevant timestamp and ending with the latest. Here's how to do that for your query.
SELECT '2014-05-01' + INTERVAL (cardinal.n * 15) MINUTE as t_stamp
FROM cardinal
WHERE cardinal.n <= 24*4
Then you need to JOIN that virtual table to your existing query, as follows
SELECT DATE_FORMAT(t_stamp.t_stamp, '%m/%d/%Y %l:%i %p') t_stamp,
ROUND(AVG(CASE WHEN id = '001' THEN value END),2) Value1,
ROUND(AVG(CASE WHEN id = '002' THEN value END),2) Value2,
ROUND(AVG(CASE WHEN id = '003' THEN value END),2) Value3
FROM table1 AS t
LEFT JOIN (
SELECT '2014-05-01' + INTERVAL (cardinal.n * 15) MINUTE as t_stamp
FROM cardinal
WHERE cardinal.n <= 24*4
) AS t_stamp
ON t_stamp.t_stamp = FROM_UNIXTIME(t.t_stamp/1000)
WHERE tagid IN ("001", "002", "003")
AND date(from_unixtime(t_stamp/1000)) BETWEEN "2014-05-01" AND "2014-05-01"
GROUP BY DATE(from_unixtime(t_stamp/1000)),
HOUR(from_unixtime(t_stamp/1000)),
MINUTE(from_unixtime(t_stamp/1000)) DIV 15
Notice that the LEFT JOIN makes sure the rows will NULL values from your original query get included in the result set.
Now, where does this magical cardinal table come from?
You can generate it as two views, like this. This particular view generates numbers from 0 to 100 000, which is more than enough for quarters of hours for a year.
CREATE OR REPLACE VIEW cardinal10 AS
SELECT 0 AS N UNION
SELECT 1 AS N UNION
SELECT 2 AS N UNION
SELECT 3 AS N UNION
SELECT 4 AS N UNION
SELECT 5 AS N UNION
SELECT 6 AS N UNION
SELECT 7 AS N UNION
SELECT 8 AS N UNION
SELECT 9 AS N;
CREATE OR REPLACE VIEW cardinal AS
SELECT A.N + 10*(B.N + 10*(C.N + 10*(D.N + 10*(E.N)))) AS N
FROM cardinal10 A,cardinal10 B,cardinal10 C,
cardinal10 D,cardinal10 E;
Here's a writeup on the topic.
http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/