I have some queries with UNION, I want to count them and show it as a row in another query
Example: I have a table called "clients", they can buy on store1, store2 or store 3, I need to show their names and how many items they bought on a row called "sales"
SELECT name,COUNT(*) FROM(
SELECT 1 FROM store1 WHERE store1.client=clients.id
UNION
SELECT 1 FROM store2 WHERE store2.client=clients.id
UNION
SELECT 1 FROM store3 WHERE store3.client=clients.id
) sales
FROM clients
If john bought 2 items from store 2 and 1 item from store 3, and mary didn't bought anything, The expected result is something like:
name | sales
------------
john | 3
mary | 0
But what I have is this error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'FROM clients'
This is another attempt using another select subquery:
SELECT name,(
SELECT COUNT(*) FROM(
SELECT 1 FROM store1 WHERE store1.client=clients.id
UNION
SELECT 1 FROM store2 WHERE store2.client=clients.id
UNION
SELECT 1 FROM store3 WHERE store3.client=clients.id
) xxxx -- (mandatory table name)
) sales
FROM clients
This give me this error:
Unknown column 'clients.id' in 'where clause'
I hope you can help me, Thank you in advance!
First union the tables then filter the results and counts...
You can't reference a field more than 1 level of separation. Since store1.client is 2 levels deep and clients is at level 0, you're more than 1 level of separation and this isn't allowed.
SELECT C.name, count(1)
FROM (
SELECT 'Store1' as StoreTable, a.* FROM store1 a UNION
SELECT 'Store2', b.* FROM store2 b UNION
SELECT 'Store3', c.* FROM store3 c
) S
RIGHT JOIN clients C
on C.ID = S.Client
GROUP BY Name
This makes a few assumptions
Data structure between each store table is the same
You may need other data from stores table which is now accessible.
I might go one step further and just create a view called "Stores" joining all the "Stores" in a union to make other queries across stores simpler. and by hardcoding a "StoreTable name in the view you can always identify the source table if needed.
SELECT name,COUNT(*)
FROM clients INNER JOIN
(
SELECT client as id, 1 FROM store1
UNION
SELECT client as id, 1 FROM store2
UNION
SELECT client as id, 1 FROM store3
)
as Stores on clients.id = Stores.id
GROUP by name
Almost there. Try something like:
SELECT c.name, sum(s.qt) as qt
from clients c
join (
SELECT client.id, COUNT(*) FROM(
SELECT client, sum(1) as qt FROM store1 group by client
UNION
SELECT client, sum(1) as qt FROM store2 group by client
UNION
SELECT client, sum(1) as qt FROM store3 group by client
) as sales s on (s.client = c.id)
group by c.name
I can be confusing something around MySQL and SQLServer, I'll test and let you know if there's something different.
Update: corrected the subquery, added sum and group_by and removed the where clause. It can have performance effects if you don't want to get all clients from all stores.
Related
I'm rookie with sql. I have the following table:
task_name
status
task_01
done
task_02
failed
task_03
done
task_04
done
task_05
failed
task_06
done
task_07
failed
task_08
failed
task_09
failed
task_10
done
task_11
done
task_12
failed
task_13
done
task_14
done
I know that another option for "status" column is "pending", but for now, no row has that status.
So, I want to obtain the count of the status, including the result "pending" to be counted as "0".
I am running:
SELECT status,count(*) FROM test.data where status in ("done","failed","pending") group by status;
And the result is:
status
count(*)
done
8
failed
6
But the desired output is:
status
count(*)
done
8
failed
6
pending
0
How can I make a query to obtain that result?
Note 1: I can't create another table (like a status_label table)
Note 2: sorry for my english, my native language is spanish
Try this:
WITH
-- your input ...
indata(task,status) AS (
SELECT 'task_01','done'
UNION ALL SELECT 'task_02','failed'
UNION ALL SELECT 'task_03','done'
UNION ALL SELECT 'task_04','done'
UNION ALL SELECT 'task_05','failed'
UNION ALL SELECT 'task_06','done'
UNION ALL SELECT 'task_07','failed'
UNION ALL SELECT 'task_08','failed'
UNION ALL SELECT 'task_09','failed'
UNION ALL SELECT 'task_10','done'
UNION ALL SELECT 'task_11','done'
UNION ALL SELECT 'task_12','failed'
UNION ALL SELECT 'task_13','done'
UNION ALL SELECT 'task_14','done'
)
,
-- add an in-line three row table with the three statuses
status(status) AS (
SELECT 'done'
UNION ALL SELECT 'failed'
UNION ALL SELECT 'pending'
)
-- left join your in-line table with the base table
-- and count the base table's statuses
SELECT
status.status
, COALESCE(count(indata.status),0) AS statuscount
FROM status
LEFT JOIN indata USING(status)
GROUP BY status.status;
status | statuscount
---------+-------------
pending | 0
failed | 6
done | 8
The simplest way is a UNION query.
SELECT status,count(*) FROM test.data group by status;
UNION
SELECT 'pending', 0
UNION allows you to combine the output of two identical queries, and even manufacture fake output for a query.
Note: This isn't a robust solution that would be used as a general solution as hard-coding results should be frowned upon, it is the simplest answer to your specific question.
I would like to have rows like a single table like the following
id name parentId
My current tables are (keys between them are foreign keys)
category
id name
which is the parent of all
and
subcategory
id name catId
and the last table which is activity
activity
id name subcatId
For the parentId of category table, it would be nothing as category is the parent of all
My attempt so far have been unsuccessful
Sample Data
category
C-1 HR null
C-2 Development null
subcategory
SC-1 Hiring C-1
SC-2 Admin C-1
SC-3 Developer C-2
activity
A-1 College Hiring SC-1
A-2 Job Fair SC-1
A-3 Java Development SC-3
Result Needed
1 HR null
2 Development null
3 Hiring C-1
4 Admin C-1
5 Developer C-2
6 College SC-1
7 Job Fair SC-1
8 Java Development SC-3
I hope it is clearer. If you need any further information, please let me know
Thanks
my attempt on 2 tables
select name
from (
select c.name
from category c
union all
select b.name
from subcategory b
inner join category ca
on ca.id = b.parentId
)
I get an error saying
Every derived table must have its own alias
Do I need to add the following lines to my query
start with parent_id is null
connect by prior id = parent_id
Try something like this:
SELECT #id := #id + 1 AS id, name, parent_id
FROM ( SELECT name, NULL AS parent_id FROM category
UNION ALL
SELECT id, name, catId FROM subcategory
UNION ALL
SELECT id, name, parentId FROM activity
) q,
( SELECT #id := 0 ) w;
Used UNION ALL to concatenate the results from multiple queries.
In UNION the column names of the first query is used as column names of the result set.
Used variable #id and the assignment operator := to generate the calculated values of the column ID.
Be advised: If you use LIMIT or OFFSET, the values of column id would not be coherent.
Actually, like this should solve your problem:
select name
from (
select c.name
from category c
union all
select b.name
from subcategory b
inner join category ca
on ca.id = b.parentId
) A -- alias is required for sub-query.
You can set anything as alias provided that it won't conflict with some of MySQL reserved word. You can set like this as well ) AS A.
For you situation, I don't think it's necessary to do sub-query or inner join. If you really want the result to be like that you can just use union all:
SELECT name,'' as ID from category
UNION ALL
SELECT name,catId FROM subcategory
UNION ALL
SELECT name,subcatId FROM activity;
Hi I'm trying to solve which elements doesn't exists in my database. In order to do so I want to compare list of integers (output from external script) with data in table. How to do such thing like:
SELECT * FROM (1,1,2,3,5,8,13...) l WHERE l NOT IN (select id from table1);
This is probably best done with a left outer join. But, your problem is creating the table of constants:
SELECT *
FROM (select 1 as id union all select 2 union all select 3 union all select 5 union all
select 8 union all select 13 union all select 21 . . .
) ids
where ids.id NOT IN (select id from table1);
This can have odd behavior, if table1.id is ever NULL. The following works more generally:
SELECT *
FROM (select 1 as id union all select 2 union all select 3 union all select 5 union all
select 8 union all select 13 union all select 21 . . .
) ids left outer join
table1 t1
on ids.id = t1.id
where t1.id is null;
EDIT:
The size of a MySQL query is dictated by the parameter max_packet_size (see here). The most recent version has a limit of 1 Gbyte. You should be able to fit 18,000 rows of:
select <n> union all
into that limit, quite easily. Gosh, I don't even think it would be 1 megabyte. I would say, though, that passing a list of 18,000 ids through the application seems inefficient. It would be nice if one database could just pull the data from the other database, without going through the application.
If your set to compare is huge I'd recommend you to create a temporary table myids with the only column id, put there all your 18K values and run query like that:
select id from myids where myids.id not in (select id from table1);
I have 5 Database, Let say their name is A B C D E
All database have the same table / structure / field
I want to get result from 5 database using table SMSOutgoing and the field is uid
It look like this :
SELECT * OR JOIN 5 database A B C D E F
FROM `table` SMSOutgoing
WHERE uid = 1
Not all the database have uid=1, it need to display which database have the result
I run SMS Gateway, each phone / 1 number using 1 database, thats why there is so many different database.
I spent hours to solve it but always error, I think i follow the wrong guide (JOIN multiple table in 1 database)
I'm Lost, please Help and Thank You
Sounds like you want to list the databases out that contain uid = 1 in the SMSOutgoing table. If so, you should be able to use UNION:
SELECT DISTINCT 'DatabaseA' WhichDb
FROM DatabaseA.SMSOutgoing
WHERE uid = 1
UNION
SELECT DISTINCT 'DatabaseB' WhichDb
FROM DatabaseB.SMSOutgoing
WHERE uid = 1
UNION
...
UNION
SELECT DISTINCT 'DatabaseF' WhichDb
FROM DatabaseF.SMSOutgoing
WHERE uid = 1
I used DISTINCT in case you could have multiple uid in the same table -- that may be unnecessary.
EDIT: From your comments, it sounds like you just want the results:
SELECT *
FROM DatabaseA.SMSOutgoing
WHERE uid = 1
UNION
SELECT *
FROM DatabaseB.SMSOutgoing
WHERE uid = 1
UNION
...
UNION
SELECT *
FROM DatabaseF.SMSOutgoing
WHERE uid = 1
You may need to use UNION ALL if you might have duplicates...
I already read (this), but couldn't figure out a way to implement it to my specific problem. I know SUM() is an aggregate function and it doesn't make sense not to use it as such, but in this specific case, I have to SUM() all of the results while maintaining every single row.
Here's the table:
--ID-- --amount--
1 23
2 11
3 8
4 7
I need to SUM() the amount, but keep every record, so the output should be like:
--ID-- --amount--
1 49
2 49
3 49
4 49
I had this query, but it only sums each row, not all results together:
SELECT
a.id,
SUM(b.amount)
FROM table1 as a
JOIN table1 as b ON a.id = b.id
GROUP BY id
Without the SUM() it would only return one single row, but I need to maintain all ID's...
Note: Yes this is a pretty basic example and I could use php to do this here,but obviously the table is bigger and has more rows and columns, but that's not the point.
SELECT a.id, b.amount
FROM table1 a
CROSS JOIN
(
SELECT SUM(amount) amount FROM table1
) b
You need to perform a cartesian join of the value of the sum of every row in the table to each id. Since there is only one result of the subselect (49), it basically just gets tacked onto each id.
With MS SQL you can use OVER()
select id, SUM(amount) OVER()
from table1;
select id, SUM(amount) OVER()
from (
select 1 as id, 23 as amount
union all
select 2 as id, 11 as amount
union all
select 3 as id, 8 as amount
union all
select 4 as id, 7 as amount
) A
--- OVER PARTITION ID
PARTITION BY which is very useful when you want to do SUM() per MONTH for example or do quarterly reports sales or yearly...
(Note needs distinct it is doing for all rows)
select distinct id, SUM(amount) OVER(PARTITION BY id) as [SUM_forPARTITION]
from (
select 1 as id, 23 as amount
union all
select 1 as id, 23 as amount
union all
select 2 as id, 11 as amount
union all
select 2 as id, 11 as amount
union all
select 3 as id, 8 as amount
union all
select 4 as id, 7 as amount
) OverPARTITIONID
Join the original table to the sum with a subquery:
SELECT * FROM table1, (SELECT SUM(amount) FROM table1 AS amount) t
This does just one sum() query, so it should perform OK:
SELECT a.id, b.amount
FROM table1 a
cross join (SELECT SUM(amount) as amount FROM table1 AS amount) b
in case someone else has the same problem and without joining we can do the following
select *
,totcalaccepted=(select sum(s.acceptedamount) from cteresult s)
, totalpay=(select sum(s.payvalue) from cteresult s)
from cteresult t
end
Using Full Join -
case when you need sum of amount field from tableB and all data from tableA on behalf of id match.
SELECT a.amount, b.* FROM tableB b
full join (
select id ,SUM(amount) as amount FROM tableA
where id = '1' group by id
) a
on a.id = b.id where a.id ='1' or b.id = '1' limit 1;