MySQL same Query multiple tables - mysql

I need to query different tables that have the same columns but different content.
Table A:
ID DocDate Type
1 2013-05-01 A
2 2013-05-01 B
3 2013-05-02 D
4 2013-05-04 D
Table B:
ID DocDate Type
1 2013-05-01 F
2 2013-05-03 G
3 2013-05-03 G
4 2013-05-05 H
What I need:
COUNT(Tablea.ID) COUNT(Tableb.ID) DocDate
2 1 2013-05-01
1 NULL 2013-05-02
NULL 2 2013-05-03
1 NULL 2013-05-04
NULL 1 2013-05-05
Any help would be really appreciated.

Try
SELECT d.docdate, a.total totala, b.total totalb
FROM
(
SELECT docdate
FROM tablea
UNION
SELECT docdate
FROM tableb
) d LEFT JOIN
(
SELECT docdate, COUNT(*) total
FROM tablea
GROUP BY docdate
) a ON d.docdate = a.docdate LEFT JOIN
(
SELECT docdate, COUNT(*) total
FROM tableb
GROUP BY docdate
) b ON d.docdate = b.docdate
 ORDER BY d.docdate
Output:
| DOCDATE | TOTALA | TOTALB |
--------------------------------
| 2013-05-01 | 2 | 1 |
| 2013-05-02 | 1 | (null) |
| 2013-05-03 | (null) | 2 |
| 2013-05-04 | 1 | (null) |
| 2013-05-05 | (null) | 1 |
Here is SQLFiddle demo

There are a couple of ways to get this result.
The most efficient query to return the specified rows is likely going to be:
SELECT NULLIF(SUM(c.cnt_a_id),0) AS cnt_a_id
, NULLIF(SUM(c.cnt_b_id),0) AS cnt_b_id
, c.DocDate
FROM (
SELECT COUNT(a.ID) AS cnt_a_id
, 0 AS cnt_b_id
, a.DocDate AS DocDate
FROM Table_A a
GROUP BY a.DocDate
UNION ALL
SELECT 0
, COUNT(b.ID)
, b.DocDate
FROM Table_B b
GROUP BY b.DocDate
) c
GROUP BY c.DocDate
Suitable covering indexes on (DocDate, ID) of each table will benefit performance on large sets.
Another simpler to understand, but more expensive, would be create the UNION of the tables, and then perform the GROUP BY.
SELECT NULLIF(COUNT(c.a_id)) AS cnt_a_id
, NULLIF(COUNT(c.b_id)) AS cnt_b_id
, c.DocDate
FROM (
SELECT a.ID AS a_id
, NULL + 0 AS b_id
, a.DocDate AS DocDate
FROM Table_A a
UNION ALL
SELECT NULL + 0 AS a_id
, b.ID AS b_id
, b.DocDate AS DocDate
FROM Table_B b
) c
GROUP BY c.DocDate
(This second query is less efficient, because of the way MySQL materializes the query in the inline view as a temporary MyISAM table; this second query basically creates a copy of Table_A and Table_B concatenated together, and runs a query against that.
The first query is little different, in that it produces smaller sets to be concatenated together.

Related

Formating RIGHT JOIN query with multiple joins

I have SQL query which works:
SELECT table1.bike_id
FROM
(
SELECT bike_id
FROM `bike_filters`
WHERE (`bike_category_id` in (416,11111))
) as table1
RIGHT JOIN (
SELECT bike_id
FROM `bike_filters`
WHERE (`bike_category_id` in (5555,779))
) as table2 ON table1.bike_id = table2.bike_id
GROUP BY bike_id
But I need to add more RIGHT JOINs lines, maybe 5 or more. How to form the query in the right way? I'm searching in the same table, but joining several records in one query to get bike_id, which fits all conditions.
The purpose of this query is to get bike_ids, which has all parameters by the query - bike can have 20 filters, but if user searches by 5 and bike matches them, we get bike_id by this query.
Table Structure:
| id | bike_id | bike_category_id |
| 1 | 3 | 416 |
| 2 | 3 | 779 |
| 3 | 3 | 344 |
| 4 | 3 | 332 |
| 5 | 4 | 444 |
| 5 | 5 | 555 |
I need something like this, this one is incorrect:
SELECT table1.bike_id
FROM
(
SELECT bike_id
FROM `bike_filters`
WHERE (`bike_category_id` IN (416,11111))
) AS table1
RIGHT JOIN (
SELECT bike_id
FROM `bike_filters`
WHERE (`bike_category_id` IN (5555,779))
) AS table2
RIGHT JOIN (
SELECT bike_id
FROM `bike_filters`
WHERE (`bike_category_id` IN (5555,344))
) AS table3
RIGHT JOIN (
SELECT bike_id
FROM `bike_filters`
WHERE (`bike_category_id` IN (5555,332))
) AS table4
GROUP BY bike_id
You can use aggregation, and put all the conditions in the HAVING clause, as follows:
SELECT bike_id
FROM bike_filters
GROUP BY bike_id
HAVING
MAX(bike_category_id in (416,11111)) = 1
AND MAX(bike_category_id in (5555,779)) = 1
This will return all bike_ids that :
have category 416 or 11111
and have category 5555 or 779
You can extend the HAVING clause as per your requirements.

How to join and give a default if a value is in one table but not another?

I am using MySQL, and I'm a newbie!
Hope you guys here can help me with a SQL question.
Say I have 2 tables, and I want to a simple join.
Table 1:
id | service_id | user_number
----------------------------------------------------------------------
0 | 1001 | 10
1 | 1002 | 20
2 | 1004 | 40
Table 2:
id | service_id | error_number
----------------------------------------------------------------------
0 | 1001 | 1000
1 | 1003 | 3000
2 | 1004 | 4000
I want to do a join on service_id and have default value of user_number and error_number to be 0 if it does not exist.
So:
id | service_id | user_number | error_number
----------------------------------------------------------------------
0 | 1001 | 10 | 1000
1 | 1002 | 20 | 0
3 | 1003 | 0 | 3000
2 | 1004 | 40 | 4000
I tried some queries, but they kept giving me null instead of 0.
Thanks a lot.
Here you should use union first, then do aggregation:
select t.`service_id`, sum(t.`user_number`) as `user_number`, sum(t.`error_number`) as `error_number`
from (
select `service_id`, `user_number`, 0 as `error_number` from t1
union
select `service_id`, 0 as `user_number`, `error_number` from t2
) t
group by `service_id`
demo here.
You can try this one, mate:
SELECT
t1.id,
t1.service_id,
COALESCE(tb1.user_number, 0) `user_number`,
COALESCE(tb2.error_number, 0) `error_number`
FROM
(
SELECT id, service_id
FROM table1
UNION
SELECT id, service_id
FROM table2
) t1
LEFT JOIN table1 tb1 ON tb1.service_id = t1.service_id
LEFT JOIN table2 tb2 ON tb2.service_id = t1.service_id;
Try this:
select COALESCE(t1_service,t2_service ) as service_id, COALESCE(user_number,0) as user_number , COALESCE(error_number,0) as error_number
from (
select t1.service_id as t1_service , t1.user_number , t2.error_number, t2.service_id as t2_service
from table_1 t1
LEFT OUTER JOIN table_2 t2
on t1.service = t2.service
union
select t1.service_id as t1_service , t1.user_number , t2.error_number, t2.service_id as t2_service
from table_1 t1
Right OUTER JOIN table_2 t2
on t1.service = t2.service
)z1
order by service_id

MySQL updating all records having max value within group

Apologies if this has been answered elsewhere and I'm just not seeing it; this is the closest I've found, but it isn't quite what I'm trying to do.
MySQL - updating all records to match max value in group
I have a table on a production web server with about 15,000 rows. There are many records sharing an item_name with another record, but item_meters is (normally, but not always) a unique value for each row. item_id is always unique. Currently every record has a value of "0" in the item_flag column.
I would like to update all records with the largest item_meters value within each item_name group to have an item_flag value of "1".
Here is a simplified version of the table ordered by item_id ASC:
----------------------------------------------
mytable
----------------------------------------------
item_id | item_name | item_meters | item_flag
--------+-----------+-------------+-----------
001 | aaa | 224 | 0
002 | aaa | 359 | 0
003 | aaa | 456 | 0
004 | bbb | 489 | 0
005 | bbb | 327 | 0
006 | bbb | 215 | 0
007 | ccc | 208 | 0
008 | ccc | 756 | 0
009 | ccc | 756 | 0
--------+-----------+-------------+-----------
The desired result would be a table with "1" in the item_flag column for each "aaa" having the largest item_meters, each "bbb" having the largest item_meters, each "ccc" having the largest item_meters, etc. like this:
----------------------------------------------
mytable
----------------------------------------------
item_id | item_name | item_meters | item_flag
--------+-----------+-------------+-----------
001 | aaa | 224 | 0
002 | aaa | 359 | 0
003 | aaa | 456 | 1
004 | bbb | 489 | 1
005 | bbb | 327 | 0
006 | bbb | 215 | 0
007 | ccc | 208 | 0
008 | ccc | 756 | 1
009 | ccc | 756 | 0
--------+-----------+-------------+-----------
(In case there are 2 or more records having the same item_name and the same item_meters (e.g. item_id 008 and 009 above), the desired result would be for the record with the numerically lower item_id (item_id is always unique), to have an item_flag value of "1" while the row with a numerically higher item_id would still have an item_flag value of "0")
Also of note, even though this database is running behind a production web server with new rows added every day, there will be no need to update the table every time a new row is added. It is something that will only be required once, regardless of whether new rows are later added outside of the parameters. The reason I mention this, is because execution speed is not a big concern since the query will only be executed once.
Thank you in advance! Please let me know if I can provide more info or clarify my question in any way.
The approach I take is to first write a query (SELECT statement) that will return the item_id values of the rows we want to update.
As a starting point, get the maximum value for item_meters, a simple query like this:
SELECT m.item_name
, MAX(m.item_meters) AS max_item_meters
FROM my_table m
GROUP BY m.item_name
We can use that query as an inline view in another query, to get the lowest item_id for each of those item_name
SELECT MIN(o.item_id) AS min_item_id
FROM ( SELECT m.item_name
, MAX(m.item_meters) AS max_item_meters
FROM my_table m
GROUP BY m.item_name
) n
JOIN my_table o
ON o.item_name = n.item_name
AND o.item_meters = n.max_item_meters
GROUP BY o.item_name, o.item_meters
And we can use that query as an inline view that gets the whole row associated with the item_id values we returned...
SELECT t.item_id
, t.item_name
, t.item_meters
, t.item_flag
FROM my_table t
JOIN ( SELECT p.min_item_id
FROM ( SELECT MIN(o.item_id) AS min_item_id
FROM ( SELECT m.item_name
, MAX(m.item_meters) AS max_item_meters
FROM my_table m
GROUP BY m.item_name
) n
JOIN my_table o
ON o.item_name = n.item_name
AND o.item_meters = n.max_item_meters
GROUP BY o.item_name, o.item_meters
) p
) q
ON q.min_item_id = t.item_id
Once the SELECT query is working, convert that to an UPDATE statement... replace the SELECT ... FROM with UPDATE, and add a SET clause. (Sometimes, it's necessary to wrap the inline view in yet another SELECT, to avoid MySQL error about disallowing references to the table we are updating.)
UPDATE my_table t
JOIN ( SELECT p.min_item_id
FROM ( SELECT MIN(o.item_id) AS min_item_id
FROM ( SELECT m.item_name
, MAX(m.item_meters) AS max_item_meters
FROM my_table m
GROUP BY m.item_name
) n
JOIN my_table o
ON o.item_name = n.item_name
AND o.item_meters = n.max_item_meters
GROUP BY o.item_name, o.item_meters
) p
) q
ON q.min_item_id = t.item_id
SET t.item_flag = '1'
If the intent is not to UPDATE the existing table, but to return a resultset, we can write a query and do an outer join to that same inline view, and return either a 0 or a 1 for item_flag, testing whether the item_id matches one we want flagged as 1...
SELECT t.item_id
, t.item_name
, t.item_meters
, IF(q.min_item_id IS NULL,0,1) AS `item_flag`
FROM my_table t
LEFT
JOIN ( SELECT p.min_item_id
FROM ( SELECT MIN(o.item_id) AS min_item_id
FROM ( SELECT m.item_name
, MAX(m.item_meters) AS max_item_meters
FROM my_table m
GROUP BY m.item_name
) n
JOIN my_table o
ON o.item_name = n.item_name
AND o.item_meters = n.max_item_meters
GROUP BY o.item_name, o.item_meters
) p
) q
ON q.min_item_id = t.item_id

How to replace a UNION in a query for a view?

I have created the following query to use in a view
SELECT
*
FROM
customers c
JOIN
customer_business cb
ON
c.customer_id = cb.customer_id
union
SELECT
*
FROM
customers c
LEFT JOIN
customer_business
ON
business_id=NULL;
It makes his work perfectly. It shows all customers with the business associated, and at the end, shows all customers with the info of the business in null.
customer_id | business_id
--------------------------------
1 | 1
2 | 1
2 | 2
1 | NULL
2 | NULL
3 | NULL
But the problem es that the UNION makes the view has very poor performace.
I tryed to do it with LEFT JOIN but doesnt shows al the customers with business in null, just the ones without any businesses associated
I know that the solution to speed up my view is to remove that UNION, but i cant figure out how.
Can anyone help me?
Thanks
EDIT
Here's an example
Customer Table
customer_id | name
--------------------------------
1 | test1
2 | test2
3 | test3
Customer_business Table
customer_business_id | customer_id | business_id
----------------------------------------------------------
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 2
Expected query result:
name | customer_id | business_id
----------------------------------------------------------
test1 | 1 | 1
test1 | 1 | 2
test1 | 1 | 3
test2 | 2 | 1
test2 | 2 | 2
test1 | 1 | NULL
test2 | 2 | NULL
test3 | 3 | NULL
Updating it based on the comments below and the output you want.
Note that I have used UNION ALL which is faster than UNION as UNION uses DISTINCT to get unique records which in your case doesn't apply. Also, make sure customer_id is PK in Customer table and try adding non-unique index on customer_id in Customer_Business table and it should help with performance.
SELECT name,
C.customer_id,
business_id
FROM Customer C
INNER JOIN Customer_Business CB
ON C.customer_id = CB.customer_id
UNION ALL
SELECT name,
C.customer_id,
NULL
FROM Customer C
Excluding the union which we know that is not performant the other thing that slows down you query is the statement in the second query ON idbusiness = NULL.
I propose to edit you query like this and see the performance as a view:
SELECT c.customer_id, idbusiness
FROM customers c
JOIN customer_business cb ON c.customer_id = cb.customer_id
UNION
SELECT customer_id, NULL
FROM customers c
EDIT:
Looking for an alternative you could try this, it should return the same output (i've changed null values with 0) but i don't think it's faster:
SELECT c.customer_id, idbusiness
FROM customers c
INNER JOIN (
SELECT customer_id, idbusiness
FROM customer_business
UNION
SELECT 0 , 0
)b ON ( c.customer_id = b.customer_id )
OR (
b.idbusiness =0
)
Eventually you could try to put into a view only the subquery b or delete the union by putting the values 0,0 as a record in table customer_business.

count value for each unique id

I have a table
id value
1 a
2 a
3 b
4 b
5 b
6 c
My id is primary.
I have total 2 a , 3 b and 1 c. So I want to count total repeat value in each primary id which matches on it
I want this format
id value_count
1 2
2 2
3 3
4 3
5 3
6 1
Try this query:
SELECT a.id, b.valueCnt
FROM tableA a
INNER JOIN (SELECT a.value, COUNT(a.value) valueCnt
FROM tableA a GROUP BY a.value) AS B ON a.value = b.value;
Check the SQL FIDDLE DEMO
OUTPUT
| ID | VALUECNT |
|----|----------|
| 1 | 2 |
| 2 | 2 |
| 3 | 3 |
| 4 | 3 |
| 5 | 3 |
| 6 | 1 |
Try This
select id, value_count from tablename as a1
join (select count(*) as value_count, value from tablename group by value) as a2
on a1.value= a2.value
I suggest you use a subselect without any joins:
SELECT
a.id,(SELECT COUNT(*) FROM tableA WHERE value = a.value) as valueCnt
FROM tableA a
Fiddle Demo
You need to use subquery.
SELECT table.id , x.value_count
FROM table
INNER JOIN
(SELECT t1.value, count(t1.id) as value_count
FROM table t1
Group by t1.value
) x on x.value = table.value