To select matching strings in 2 tables - mysql

TableA
Id | M | D | Y |
=======================
1 | 10 | 28 | 2012 |
2 | 11 | 29 | 2012 |
3 | 12 | 30 | 2012 |
TableB
Id | M | D | Y |
=======================
4 | 09 | 28 | 2012 |
5 | 11 | 29 | 2012 |
6 | 01 | 30 | 2013 |
I will search by M and D
Ex: If matching M = 11 AND D = 29 ... so will return ID(s) 2 , 5
I can only find by one table like this
mysql_query("SELECT * FROM TableA WHERE M='11' AND Y='29' ORDER BY D ASC , Id DESC;";)
But how to find in multiple tables ?

Use the UNION clause between two SELECTs
(SELECT Id, M, D, Y FROM TableA WHERE M='11' AND Y='29')
UNION
(SELECT Id, M, D, Y FROM TableB WHERE M='11' AND Y='29')
ORDER BY D ASC , Id DESC

You better use UNION ALL (in case tableB has identical records) to get all the records. Otherwise just UNION will do.
SELECT * FROM TableA WHERE M='11' AND Y='29'
UNION ALL
SELECT * FROM TableB WHERE M='11' AND Y='29'
ORDER BY D ASC , Id DESC;

SELECT * FROM TableA WHERE M='11' AND Y='29'
UNION
SELECT * FROM TableB WHERE M='11' AND Y='29'
ORDER BY D ASC , Id DESC;

Related

Calculate sum on the records while joining two tables with a third table

I have three tables:
mysql> select * from a;
+----+---------+
| ID | Name |
+----+---------+
| 1 | John |
| 2 | Alice |
+----+---------+
mysql> select * from b;
+------+------------+----------+
| UID | date | received |
+------+------------+----------+
| 1 | 2017-10-02 | 5 |
| 1 | 2017-09-30 | 1 |
| 1 | 2017-09-29 | 4 |
+------+------------+----------+
mysql> select * from c;
+------+------------+------+
| UID | date | sent |
+------+------------+------+
| 1 | 2017-09-25 | 7 |
| 1 | 2017-09-30 | 2 |
| 1 | 2017-09-29 | 3 |
+------+------------+------+
If I try to calculate the total number of sent for John, it would be 12. And for received, it would be 10.
But if I try to join all three tables, the result is weird. Here is my query to join three tables:
mysql> select sum(sent), sum(received) from a
-> join c on c.UID = a.ID
-> join b on b.UID = a.ID
-> where a.ID = 1;
+-----------+---------------+
| sum(sent) | sum(received) |
+-----------+---------------+
| 36 | 30 |
+-----------+---------------+
But I need correct numbers (12 and 10, respectively). How can I have correct numbers?
You should join the aggregated result and not the raw tables
select a.uid, t1.received, t2.sent
from a
inner join (
select uid, sum(received) received
from b
group by uid
) t1 on t1.uid = a.id
inner join (
select uid, sum(sent) sent
from c
group by uid
) t2 on t2.uid = a.id
where a.id = 1
You could try below
select bx.id, recieved, sum(c.sent) sent from
(
SELECT a.id, sum(b.received) recieved
from a
INNER JOIN b
ON a.id=b.uid
group by a.id
) bx
INNER JOIN c
ON c.uid=bx.id
group by bx.id, bx.recieved;
>>>Demo<<<
This gets rid of the subquery, but introduces something else you might not want:
( SELECT uid, 'Received' AS direction, SUM(received) AS HowMany
WHERE uid = 1
GROUP BY uid )
UNION ALL
( SELECT uid, 'Sent' AS direction, SUM(sent) AS HowMany
WHERE uid = 1
GROUP BY uid )

fetching cumulative sum grouped by month on time interval

This the users table. from where i am fetching data and filling in the missing months by adding table d to query.
---------------------------------------
| uid | date_register |
---------------------------------------
| 1 | 2011-07-20 02:24:36 |
---------------------------------------
| 2 | 2012-10-03 07:37:43 |
---------------------------------------
| ... | ... ... ... ... ... |
---------------------------------------
| 300000 | 2015-12-19 04:13:51 |
---------------------------------------
I want to fetch cumulative sum grouped by month. I am using following query to do this.
SELECT d.y, d.m, p.cum_sum
FROM
(SELECT a.y, b.m
FROM (SELECT 2015 AS Y UNION ALL SELECT 2014 AS Y) a
CROSS JOIN (SELECT 1 AS m UNION ALL SELECT 2 AS m UNION ALL SELECT 3 AS m UNION ALL SELECT 4 AS m UNION ALL SELECT 5 AS m UNION ALL SELECT 6 AS m UNION ALL SELECT 7 AS m UNION ALL SELECT 8 AS m UNION ALL SELECT 9 AS m UNION ALL SELECT 10 AS m UNION ALL SELECT 11 AS m UNION ALL SELECT 12 AS m) b
WHERE CONCAT(a.y, '-', b.m, '-01') BETWEEN CURDATE() - INTERVAL 13 MONTH AND CURDATE()
ORDER BY 1, 2) d
LEFT OUTER JOIN
(SELECT year1,
month,
#cnt := #cnt + total cum_sum
FROM (
SELECT YEAR(date_register) year1,
MONTH(date_register) month,
COUNT(*) total
FROM users
WHERE date_register >= DATE_FORMAT(NOW() ,'%Y-%m-01') - INTERVAL 1 YEAR
AND useractivated = 1
GROUP BY YEAR(date_register), MONTH(date_register)
) n, (SELECT #cnt := count(*) from users
where date_register< DATE_FORMAT(NOW() ,'%Y-%m-01') - INTERVAL 1 YEAR AND useractivated = 1) u)
p ON d.m=p.month AND d.y=p.year1
ORDER BY 1 ASC, 2 ASC
but it returns null value if there is no transactions in that particular month. like following (sample data)
+------+-------+---------+
| y | m | cum_sum |
+------+-------+---------+
| 2014 | 10 | 12356 |
| 2014 | 11 | 13567 |
| 2014 | 12 | 14239 |
| 2015 | 01 | 15234 |
| 2015 | 02 | 16571 |
| 2015 | 03 | NULL |
| 2015 | 04 | 24239 |
| 2015 | 05 | 34239 |
| 2015 | 06 | 44239 |
| 2015 | 07 | 45239 |
| 2015 | 08 | 46239 |
| 2015 | 09 | 57239 |
| 2015 | 10 | 67239 |
+------+-------+---------+
so there was no transaction in march 2015, so it returning null but it should return 16571 like previous row. What I am doing wrong? Thanks.
Try this:
select a.y, b.m,
(
SELECT count(*) as cum_count
FROM users
WHERE (YEAR(date_register) = a.y and MONTH(date_register) <= b.m) or year(date_register) < a.y
) as cum_count
from
(
SELECT 2015 AS Y UNION ALL SELECT 2014 AS Y
) a
CROSS JOIN
(
SELECT 1 AS m UNION ALL SELECT 2 AS m UNION ALL SELECT 3 AS m UNION ALL SELECT 4 AS m UNION ALL SELECT 5 AS m UNION ALL SELECT 6 AS m UNION ALL SELECT 7 AS m UNION ALL SELECT 8 AS m UNION ALL SELECT 9 AS m UNION ALL SELECT 10 AS m UNION ALL SELECT 11 AS m UNION ALL SELECT 12 AS m
) b
order by a.y, b.m

Count total number of occurrence of two consecutive values in a table

My table structure
+----+--------+
| id | status |
+----+--------+
| 1 | 10 |
| 2 | 21 |
| 3 | 22 |
| 4 | 29 |
| 5 | 30 |
| 6 | 32 |
| 7 | 33 |
| 8 | 21 |
| 9 | 22 |
| 10 | 23 |
| 11 | 21 |
| 12 | 22 |
| 13 | 23 |
+----+--------+
I want to count total number of times when status 22 comes just after status 21.
In this case the query should return 3.
sql fiddle
Just use a Self Join with Conditional Aggregate
SELECT Sum(CASE WHEN a.status = 22 AND b.status = 21 THEN 1 END) As Stat_Count
FROM testTable a
LEFT OUTER JOIN testTable b
ON a.id = b.id + 1
SQLFIDDLE DEMO
If you can have gaps in your id's you can use a subquery to check whether the previous status of a 22 row is 21
select count(*)
from testtable a
where a.status = 22 and (select status from testtable b
where b.id < a.id order by id desc limit 1) = 21
http://sqlfiddle.com/#!2/9d567/2
Another way gets all id's of previous rows of rows with a status of 22 in derived table and then joins the ids to count how many have a status of 21
select count(*) from (
select max(b.id) max_b_id
from testtable a join testtable b on b.id < a.id
where a.status = 22
group by a.id
) t1 join testtable a on a.id = t1.max_b_id
where a.status = 21
I have tried to solve it in php
$q="";
$q= mysqli_query("select *from testTable");
while($r=mysqli_fetch_assoc($q)){
$rows[]=$r;
}
$success=0;
for ($i=0;$i<count($rows);$i++){
if($rows[$i]['status']==21 and $rows[$i+1]['status']==22 ){
$success+=1;
}
}
echo $success;

count same item between many rows and columns on one query

I have the next table:
id| C1 | C2 | C3 | C4 | C5|
----------------------------
01| 23 | 19 | 30 | 30 | 30|
---------------------------
02| 23 | 40 | 30 | 30 | 30|
----------------------------
03| 23 | 20 | 19 | 30 | 30|
----------------------------
04| 23 | 19 | 30 | 30 | 30|
----------------------------
05| 23 | 23 | 23 | 19 | 30|
----------------------------
in this table the count of number 19 is 4
I need a query to count how many times each number appears in the table, searching in many rows and columns, or at least number 19 with MySQL.
I'd union all the columns, and then just count the values:
SELECT c, COUNT(*)
FROM (SELECT c1 AS c FROM my_table
UNION ALL
SELECT c2 FROM my_table
UNION ALL
SELECT c3 FROM my_table
UNION ALL
SELECT c4 FROM my_table
UNION ALL
SELECT c5 FROM my_table) t
GROUP BY c
One option is to use IF with SUM:
SELECT SUM(IF(id=19,1,0) + IF(c1=19,1,0) + IF(c2=19,1,0) +
IF(c3=19,1,0) + IF(c4=19,1,0) + IF(c5=19,1,0))
FROM YourTable
Condensed SQL Fiddle Demo
As pointed out, the IF statement isn't actually needed:
SELECT SUM((c1=19)+(c2=19)+(c3=19)+(c4=19))
FROM YourTable
select count(*) FROM tablename where c1 = 19 OR c2 = 19 or c3 = 19 etc
I prefer the SUM method:
SELECT SUM(
(t1.c1=x.num)
+ (t1.c2=x.num)
+ (t1.c3=x.num)
+ (t1.c4=x.num)
+ (t1.c5=x.num)
) count
FROM table1 t1
JOIN (SELECT 19 num) x

How to select a row before a specific row on MySQL, if the table is ordered by date?

I have a select result like this:
ID | DATE
----------------
10 | 2014-07-23
7 | 2014-07-24
8 | 2014-07-24
9 | 2014-07-24
1 | 2014-07-25
2 | 2014-07-25
6 | 2014-07-25
3 | 2014-07-26
4 | 2014-07-27
5 | 2014-07-28
The result above is ordered by date. Now, I want to select the one previous row before:
2 | 2014-07-25
Which is:
1 | 2014-07-25
In case I don't know the exact ID and the conditional code must be compatible with if I want to select a previous row of:
3 | 2014-07-26
Which is:
6 | 2014-07-25
What condition should I use?
UPDATE
Tried this:
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, t1.*
FROM table t1
Then I got this:
RANK | ID | DATE
----------------
1 | 10 | 2014-07-23
2 | 7 | 2014-07-24
3 | 8 | 2014-07-24
4 | 9 | 2014-07-24
5 | 1 | 2014-07-25
6 | 2 | 2014-07-25
7 | 6 | 2014-07-25
8 | 3 | 2014-07-26
9 | 4 | 2014-07-27
10 | 5 | 2014-07-28
Then I tried this:
SET #rank=0;
SELECT #rank:=#rank+1 AS rank, t1.*
FROM table t1
WHERE rank < 3;
I got this error: Unknown column 'rank' in 'where clause'.
Here's one way...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(ID INT NOT NULL PRIMARY KEY
,DATE DATE NOT NULL
);
INSERT INTO my_table VALUES
(10 ,'2014-07-23'),
(7 ,'2014-07-24'),
(8 ,'2014-07-24'),
(9 ,'2014-07-24'),
(1 ,'2014-07-25'),
(2 ,'2014-07-25'),
(6 ,'2014-07-25'),
(3 ,'2014-07-26'),
(4 ,'2014-07-27'),
(5 ,'2014-07-28');
SELECT a.id
, a.date
, b.id b_id
, b.date b_date
FROM
( SELECT x.*
, COUNT(*) rank
FROM my_table x
JOIN my_table y
ON (y.date < x.date)
OR (y.date = x.date AND y.id <= x.id)
GROUP
BY x.date
, x.id
) a
LEFT
JOIN
( SELECT x.*
, COUNT(*) rank
FROM my_table x
JOIN my_table y
ON (y.date < x.date)
OR (y.date = x.date AND y.id <= x.id)
GROUP
BY x.date
, x.id
) b
ON b.rank = a.rank - 1;
+----+------------+------+------------+
| id | date | b_id | b_date |
+----+------------+------+------------+
| 10 | 2014-07-23 | NULL | NULL |
| 7 | 2014-07-24 | 10 | 2014-07-23 |
| 8 | 2014-07-24 | 7 | 2014-07-24 |
| 9 | 2014-07-24 | 8 | 2014-07-24 |
| 1 | 2014-07-25 | 9 | 2014-07-24 |
| 2 | 2014-07-25 | 1 | 2014-07-25 |
| 6 | 2014-07-25 | 2 | 2014-07-25 |
| 3 | 2014-07-26 | 6 | 2014-07-25 |
| 4 | 2014-07-27 | 3 | 2014-07-26 |
| 5 | 2014-07-28 | 4 | 2014-07-27 |
+----+------------+------+------------+
... but you can also do this (quicker) with variables.
You can add a row id to the select like this
SELECT #rowid:=#rowid+1 as rowid,
t1.* FROM yourdatabase.tablename t1, (SELECT #rowid:=0) as rowids;
Then you can run a simple query to get the lower rowid from the input.
This uses a sub query that joins the table against itself, where on one side it is the date you are checking and matching against smaller dates. It uses MAX to get the highest smaller date.
This is then joined against another sub query that gets the highest ID for each date, which also joins against the table itself to get the other details from that row.
SELECT table.*
FROM table
INNER JOIN
(
SELECT MAX(a.date) AS latest_prev_date
FROM table1 a
INNER JOIN table1 b
ON a.date > b.date
WHERE a.date = '2014-07-26'
) sub0
ON table.date = sub0.latest_prev_date
INNER JOIN
(
SELECT date, MAX(ID) AS latest_prev_id
FROM table1
GROUP BY date
) sub1
ON table.ID = sub1.latest_prev_id
AND sub1.date = sub0.latest_prev_date
if you want to use a user_defined_variable this is a way to do it.
SELECT
tab.id, temp.id, temp.date
FROM
(
SELECT
#A:=#A + 1 AS rank_col, t.date, t.id
FROM
myTable t
CROSS JOIN (SELECT #A:=0) join_table
) AS tab
LEFT JOIN
(
SELECT
#B:=#B + 1 AS rank_col, t2 . *
FROM myTable t2
CROSS JOIN (SELECT #B:=0) join_table1
) temp ON temp.rank_col = tab.rank_col - 1;
DEMO