How to count and join two tables on mySQL from different database? - mysql

I have two databases. Those are core and push.
This is subs table from core database:
+----+---------+----+----+-----+
| id | service | ad | op | act |
+----+---------+----+----+-----+
| 1 | CHO | 96 | x | 1 |
| 2 | CHO | 98 | x | 1 |
| 3 | DANG | 96 | x | 1 |
| 4 | HOORAY | 96 | x | 1 |
| 5 | CHO | 98 | x | 1 |
| 6 | DANG | 96 | x | 1 |
+----+---------+----+----+-----+
and this is the buff table from push database:
+----+---------+-------+
| id | service | sub |
+----+---------+-------+
| 1 | DANG | daily |
| 2 | HOORAY | daily |
| 3 | DANG | daily |
+----+---------+-------+
i want to count subs and buff. i made this query for subs:
select service, ad, op, count(1) as sub where act=1 group by service,ad,op;
RESULT:
+---------+----+----+-----+
| service | ad | op | sub |
+---------+----+----+-----+
| CHOO | 96 | x | 1 |
| CHOO | 98 | x | 2 |
| HOORAY | 96 | x | 1 |
| DANG | 96 | x | 2 |
+---------+----+----+-----+
and this is the query for buff:
select service, count(1) as pushed
from buff a where sub = 'daily'
group by service;
RESULT:
+---------+---------+
| service | pushed |
+---------+---------+
| HOORAY | 1 |
| DANG | 2 |
+---------+---------+
So, my question is how to join those tables from two different databases?
here is the result that i expect:
+---------+----+----+-----+--------+
| service | ad | op | sub | pushed |
+---------+----+----+-----+--------+
| CHOO | 96 | x | 1 | 0 |
| CHOO | 98 | x | 2 | 0 |
| HOORAY | 96 | x | 1 | 1 |
| DANG | 96 | x | 2 | 2 |
+---------+----+----+-----+--------+

One option is to join together your two current subqueries. I have used a full outer join below, because we want to handle the possibity that a service may appear in only the subs or buff table, but not both.
SELECT t1.service,
t1.ad,
t1.op,
t1.sub,
COALESCE(t2.pushed, 0) AS pushed
FROM
(
SELECT service, ad, op, COUNT(*) AS sub
FROM subs
WHERE act = 1
GROUP BY service,ad,op
) t1
LEFT JOIN
(
SELECT service, COUNT(1) AS pushed
FROM buff a
WHERE sub = 'daily'
GROUP BY service
) t2
ON t1.service = t2.service
UNION
SELECT t2.service,
COALESCE(t1.ad, -1),
COALESCE(t1.op, -1),
COALESCE(t1.sub, -1),
t2.pushed
FROM
(
SELECT service, ad, op, COUNT(*) AS sub
FROM subs
WHERE act = 1
GROUP BY service,ad,op
) t1
RIGHT JOIN
(
SELECT service, COUNT(1) AS pushed
FROM buff a
WHERE sub = 'daily'
GROUP BY service
) t2
ON t1.service = t2.service

use database name as table name prefix with dot, I don't check the below sentence, it only show the idea:
SELECT service, ad, op, count(1) AS sub FROM core.subs AS t1
LEFT JOIN push.buff AS t2 ON t1.service=t2.service WHERE t1.act=1
AND sub = 'daily' GROUP BY t1.service,t1.ad,t1.op

Related

Selecting COUNT and MAX columns with 2 tables and a bridge table

so what I am trying to do is having 3 tables (pictures, collections, and bridge) with the following columns:
Collections Table:
| id | name |
------------------
| 1 | coll1 |
| 2 | coll2 |
------------------
Pictures Table: (timestamps are unix timestamps)
| id | name | timestamp |
-------------------------
| 5 | Pic5 | 1 |
| 6 | Pic6 | 19 |
| 7 | Pic7 | 3 |
| 8 | Pic8 | 892 |
| 9 | Pic9 | 4 |
-------------------------
Bridge Table:
| id | collection | picture |
-----------------------------
| 1 | 1 | 5 |
| 2 | 1 | 6 |
| 3 | 1 | 7 |
| 4 | 1 | 8 |
| 5 | 2 | 5 |
| 6 | 2 | 9 |
| 7 | 2 | 7 |
-----------------------------
And the result should look like this:
| collection_name | picture_count | newest_picture |
----------------------------------------------------
| coll1 | 4 | 8 |
| coll2 | 3 | 9 |
----------------------------------------------------
newest_picture should always be the picture with the heighest timestamp in that collection and I also want to sort the result by it. picture_count is obviously the count of picture in that collection.
Can this be done in a single statement with table joins and if yes:
how can I do this the best way?
A simple method uses correlated subqueries:
select c.*,
(select count(*)
from bridge b
where b.collection = c.id
) as pic_count,
(select p.id
from bridge b join
pictures p
on b.picture = b.id
where b.collection = c.id
order by p.timestamp desc
limit 1
) as most_recent_picture
from collections c;
A more common approach would use window functions:
select c.id, c.name, count(bp.collection), bp.most_recent_picture
from collections c left join
(select b.*,
first_value(p.id) over (partition by b.collection order by p.timestamp desc) as most_recent_picture
from bridge b join
pictures p
on b.picture = p.id
) bp
on bp.collection = c.id
group by c.id, c.name, bp.most_recent_picture;

MySQL Data Summary/Counts Table

I'm trying to put together a summary table that has counts of types of mail sent by group.
Hopefully the below is enough to explain what I mean.
Table 1 (senders)
| id | name | group_id |
+----+------+----------+
| 1 | mike | 1 |
| 2 | john | 1 |
| 3 | lucy | 2 |
| 4 | lobo | 3 |
Table 2 (mail)
| id | type | sender_id |
+----+----------+-----------+
| 1 | letter | 1 |
| 2 | postcard | 2 |
| 3 | postcard | 1 |
| 4 | letter | 2 |
| 5 | postcard | 2 |
| 6 | postcard | 4 |
Table 3 (groups)
| id | name | active |
+----+-------+--------+
| 1 | alpha | 1 |
| 2 | black | 1 |
| 3 | cero | 0 |
Ideal result
| group | letter | postcard | parcel |
+-------+--------+----------+--------+
| alpha | 2 | 3 | 0 |
| black | 0 | 0 | 0 |
So I need to get counts per mail type for active groups.
I've been working through examples (only learning MySQL) but when I think of this situation I'm just totally blank.
Have looked at the answers to Joining three tables to get summary data in MySQL but I don't quite understand how to translate the answers to my problem.
Any help is appreciated.
SELECT t.name,
MAX(CASE t.TYPE WHEN 'letter' THEN #CS:=#CS+1 ELSE 0 END ) letter,
MAX(CASE t.TYPE WHEN 'postcard' THEN #CS1:=#CS1+1 ELSE 0 END ) postcard ,
MAX(CASE t.TYPE WHEN 'parcel ' THEN #CS2:=#CS2+1 ELSE 0 END ) parcel
FROM
(SELECT
groups. name,
mail.type
FROM
groups
LEFT JOIN senders ON groups.id = senders.id
LEFT JOIN mail ON senders.id = mail.sender_id ) AS t
,(SELECT #CS:=0) CS ,(SELECT #CS1:=0) CS1 ,(SELECT #CS2:=0) CS2
You put this query
Select count(*) from senders s inner join mail m on s.id = s.sender_id inner join
groups g on s.groups_id = g.id group by m.type

Select most recent MAX() and MIN() - WebSQL

i'm build an exercises web app and i'm working with two tables like this:
Table 1: weekly_stats
| id | code | type | date | time |
|----|--------------|--------------------|------------|----------|
| 1 | CC | 1 | 2015-02-04 | 19:15:00 |
| 2 | CC | 2 | 2015-01-28 | 19:15:00 |
| 3 | CPC | 1 | 2015-01-26 | 19:15:00 |
| 4 | CPC | 1 | 2015-01-25 | 19:15:00 |
| 5 | CP | 1 | 2015-01-24 | 19:15:00 |
| 6 | CC | 1 | 2015-01-23 | 19:15:00 |
| .. | ... | ... | ... | ... |
Table 2: global_stats
| id | exercise_number |correct | wrong |
|----|-----------------|--------|-----------|
| 1 | 138 | 1 | 0 |
| 2 | 246 | 1 | 0 |
| 3 | 988 | 1 | 10 |
| 4 | 13 | 5 | 0 |
| 5 | 5 | 4 | 7 |
| 6 | 5 | 4 | 7 |
| .. | ... | ... | ... |
What i would like is to get MAX(correct-wrong) and MIN(correct-wrong) and now i'm working with this query:
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
UNION
SELECT
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats);
This query is working good, except for one thing: when "WHERE correct - wrong = (SELECT MIN(correct - wrong)[...]" selects more than one row, the row selected is the first but i would like to have returned the most recent (in other words: ordered by datetime(date, time)). Is it possible?
Thanks!
I think you can solve it like this:
SELECT * FROM (
SELECT
1 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MAX(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1 ) as a
UNION
SELECT * FROM (
SELECT
2 as sort_column,
exercise_number,
date,
time
FROM weekly_stats AS w JOIN global_stats AS g
ON w.id=g.id
WHERE correct - wrong = (SELECT MIN(correct - wrong) from global_stats)
ORDER BY date DESC, time DESC
LIMIT 1) as b
ORDER BY sort_column;
Here is the documentation about how UNION works.

SQL Query design involving multiple tables

I am working on a MYSQL query design that, in my opinion, is pretty hard. I'm not experienced in SQL, so I found it really dificult. The point is:
I've got the 'ordertable' table which stores the order of some codes (AA, BB, CC..). In another table, 'AllTables' I store the name of a table associated to a code (AA -> tableA). Finally, 'tableA' table stores some data of diferent units (unit1, unit2...).
CASE 1.
ordertable : Order of codes is given like:
+----------------+------+
| split_position | code |
+----------------+------+
| 1 | AA |
| 2 | BB |
| 3 | CC |
| 4 | DD |
+----------------+------+
CASE 2.
ordertable Order of codes is given like:
+-------+------+------+------+------+
| id | pos1 | pos2 | pos3 | pos4 |
+-------+------+------+------+------+
| unit1 | AA | BB | DD | CC |
| unit2 | CC | BB | AA | DD |
| unit3 | BB | DD | CC | AA |
+-------+------+------+------+------+
In Case 2 we can also find special codes like 'var15':
+-------+------+-------+------+-------+
| id | pos1 | pos2 | pos3 | pos4 |
+-------+------+-------+------+-------+
| unit1 | AA | var15 | DD | var37 |
| unit2 | CC | BB | AA | DD |
+-------+------+-------+------+-------+
In case we find something similar to 'var'+ number the associated table is always the same: 'variable', where de 'id' is the number of the code 'var37' -> id = 37.
variable
+-----+------------+------+--------+
| id | name | time | active |
+-----+------------+------+--------+
| 15 | Pedro | 5 | 1 |
| 17 | Maria | 4 | 1 |
+-----+------------+------+--------+
Info of tables:
AllTables
+------+------------+
| code | name |
+------+------------+
| AA | tableA |
| BB | tableB |
| CC | tableC |
| DD | tableD |
+------+------------+
tableA
+-------+------+------+--------+
| id | name | time | active |
+-------+------+------+--------+
| unit1 | Mark | 11 | 1 |
| unit2 | Jame | 20 | 0 |
+-------+------+------+--------+
tableB
+-------+------+------+--------+
| id | name | time | active |
+-------+------+------+--------+
| unit1 | Mari | 44 | 1 |
| unit3 | nam2 | 57 | 1 |
+-------+------+------+--------+
Given an id='unit1', I'm expecting the next:
Result
+----------------+------+-------+-------+--------+
| split_position | code | name | time | active |
+----------------+------+-------+-------+--------+
| 1 | AA | Mark | 11 | 1 |
| 2 | BB | Mari | 44 | 1 |
| 3 | CC | | | 0 |
| 4 | DD | | | 0 |
+----------------+------+-------+-------+--------+
In case that the id (unit1) does not exists in tableC or tableD, 'split_position' and 'code' associated should appear but in the 'active' field should appear a 0.
it's a bit of a steep learning curve, but basically you have to declare a cursor and loop
over the each row in the ordertable and select your data then UNION the result together using dynamic SQL.
check this sqlFiddle
to order by final result by split position ASC just add ORDER BY split_position ASC to the sql variable before executing it like this sqlFiddle
to solve your problem you would need something like the following:
select split_position, code, name, time, active
from
(
select 'tableA' as tablename, id, [name], [time], active
from tableA
union all select 'tableB' as tablename, id, [name], [time], active
from tableB
) as tbls
inner join alltables atbls
on tbls.tablename=atbls.name
inner join ordertable ot
on atbls.code=ot.code
where tbls.id='unit1'

Create view from multiple queries

I'm currently doing a sql script using the following tables in a database:
Menu table (contains the information about dishes):
+----------+--------------+--------+-------------+
| id_plato | nombre_plato | precio | tipo |
+----------+--------------+--------+-------------+
| 1 | peces | 100 | entrada |
| 2 | caca | 20 | rolls_fondo |
| 3 | plato1 | 200 | bajativo |
| 4 | plato2 | 200 | entrada |
| 5 | plato3 | 200 | entrada |
| 6 | plato4 | 200 | entrada |
| 7 | plato5 | 200 | entrada |
| 8 | plato6 | 200 | entrada |
| 9 | plato7 | 200 | entrada |
| 10 | plato8 | 200 | entrada |
| 11 | plato9 | 200 | entrada |
| 12 | plato10 | 200 | entrada |
| 13 | plato11 | 200 | entrada |
| 14 | plato1 | 200 | entrada |
+----------+--------------+--------+-------------+
Boleta table (contains the informatio of all sales):
+-----------+------+--------------+
| id_boleta | sexo | precio_final |
+-----------+------+--------------+
| 1 | m | 1 |
| 2 | f | 1 |
| 3 | f | 1 |
| 4 | m | 1 |
+-----------+------+--------------+
BoletaDetalle table (contains the information of each dish bought on a given sale):
+------------+-----------+----------+----------------+
| id_detalle | id_boleta | id_plato | precio_detalle |
+------------+-----------+----------+----------------+
| 1 | 1 | 1 | 1 |
| 2 | 1 | 1 | 1 |
| 3 | 1 | 2 | 1990 |
| 4 | 2 | 1 | 5 |
| 5 | 3 | 4 | 1 |
| 6 | 4 | 2 | 1 |
| 7 | 5 | 4 | 1 |
| 8 | 4 | 2 | 1 |
+------------+-----------+----------+----------------+
Basically, this is a small program for a small restaurant and I have been asked to show a quick report trough an sql script that displays a view containing the favorite dish of a certain group of costumers. For example, based on costumer's gender as follows:
| Sex | Type |
+------------------+
| M | entrada |
| F | bajativo |
I'm having a lot of trouble trying to figure this out despite of it being an easy task.
This is what I have so far:
DROP VIEW IF EXISTS v1;
CREATE VIEW v1 (Tipo, CantidadMujer, CantidadHombre)
AS
SELECT m.tipo as Tipo, COUNT(bd.id_plato) as 'Cantidad Mujer', COUNT(bd2.id_plato) as 'Cantidad Hombre'
FROM Menu m
LEFT JOIN BoletaDetalle bd
INNER JOIN Boleta b
on (bd.id_boleta = b.id_boleta AND b.sexo = 'f')
on bd.id_plato = m.id_plato AND m.tipo = 'entrada'
LEFT JOIN BoletaDetalle bd2
INNER JOIN Boleta b2
on (bd2.id_boleta = b2.id_boleta AND b2.sexo = 'm')
on bd2.id_plato = m.id_plato AND m.tipo = 'entrada'
GROUP BY(m.tipo);
SELECT * FROM v1;
I was thinking of creating a view like the one above with the amount of dishes bought per each gender (on each dish type) and then create a second view (the final one) by getting the maximum value per category. I'm really lost here so any help would be appreciated.
Are you looking for something like this?
CREATE VIEW sexo_tipo_view AS
SELECT b.sexo, m.tipo, COUNT(d.id_plato) total
FROM boletadetalle d JOIN boleta b
ON d.id_boleta = b.id_boleta JOIN menu m
ON d.id_plato = m.id_plato
GROUP BY b.sexo, m.tipo
ORDER BY sexo, total DESC;
CREATE VIEW sexo_tipo_favorites_view AS
SELECT sexo, tipo
FROM sexo_tipo_view
GROUP BY sexo;
SELECT s.sexo, COALESCE(v.tipo, '-') tipo
FROM
(
SELECT 'f' sexo UNION ALL
SELECT 'm'
) s LEFT JOIN sexo_tipo_favorites_view v
ON s.sexo = v.sexo;
or without views
SELECT s.sexo, COALESCE(v.tipo, '-') tipo
FROM
(
SELECT 'f' sexo UNION ALL
SELECT 'm'
) s LEFT JOIN
(
SELECT sexo, tipo
FROM
(
SELECT b.sexo, m.tipo, COUNT(d.id_plato) total
FROM boletadetalle d JOIN boleta b
ON d.id_boleta = b.id_boleta JOIN menu m
ON d.id_plato = m.id_plato
GROUP BY b.sexo, m.tipo
ORDER BY sexo, total DESC
) a
GROUP BY sexo
) v
ON s.sexo = v.sexo;
Sample output (in both cases):
| SEXO | TIPO |
|------|-------------|
| f | entrada |
| m | rolls_fondo |
Here is SQLFiddle demo