I have two tables, customer and customer_order
customer
cust_id, cust_name
121 Acme Wholesalers
234 Griffen Electric
336 East Coast Marine Supplies
544 Sanford Automotive
customer_orders
order_num,cust_id,order_date
1 121 2019-01-15
2 234 2019-07-24
3 336 2020-05-02
4 121 2019-01-15
5 336 2020-03-19
6 234 2019-07-24
7 121 2019-01-15
8 336 2020-06-12
I need to find out the name of each customer who has placed exactly 3 orders but using a non-correlated subquery and without JOINS
I have got the same query results using correlated query
SELECT c.cust_name FROM customer c WHERE cust_id
IN (SELECT GROUP_CONCAT(cust_id) FROM customer_order )
but this time I need same results using non-correlated and without join also
seems like the big rock is finding cust_id values that have exactly three occurrences in customer_order we can do something like this
SELECT o.cust_id
FROM customer_orders o
GROUP
BY o.cust_id
HAVING COUNT(*) = 3
referencing the aggregate in a HAVING clause to restrict rows are returned.
we could use this query as an inline view (or, derived table, in the the mysql parlance), and then
SELECT n.cust_id
FROM (
-- cust_id that have exactly three rows
SELECT o.cust_id
FROM customer_orders o
GROUP
BY o.cust_id
HAVING COUNT(*) = 3
) n
and typically do a JOIN to the customers table to retrieve the name.
given a weird restriction of "no joins", we could (less optimally) use an WHERE foo IN (subquery) form
Related
Sorry if the title is not well explained tried my best.
I currently have this transactions table which hold the records, every row has an agent and a currency assigned to it.
id
amount
agent
currency_id
1
400.00
agent1
1
2
170.00
agent5
3
3
110.00
agent4
2
4
430.00
agent5
3
5
155.00
agent1
1
6
370.00
agent2
2
7
10.00
agent2
2
8
150.00
agent1
1
9
130.00
agent3
3
10
445.00
agent4
2
And this other table called currency which holds the unique currency and name.
id
currency
1
USD
2
VES
3
EUR
The query that I want to make is a SUM and group by agent for every currency there is. I am able to do it with a single query like this but only for one currency in the WHERE clause:
SELECT a.agent,
SUM(a.amount)
FROM transactions AS a
INNER JOIN currency AS b ON b.id = a.currency_id
WHERE b.currency = 'VES'
GROUP BY a.agent
I will be getting this result which is only for the VES currency
agent
total
agent2
380.00
agent4
555.00
I am looking for one query that allow me to get the result of all 3 current existing currencies (USD, VES, EUR) this should give a result of 3 different tables
I suspect that you want a report showing all agents, currencies, and their sums. You may try using this cross join approach:
SELECT a.agent, c.currency, COALESCE(SUM(t.amount), 0) AS total
FROM (SELECT DISTINCT agent FROM transactions) a
CROSS JOIN currency c
LEFT JOIN transactions t
ON t.agent = a.agent AND
t.currency_id = c.id
GROUP BY 1, 2
ORDER BY 1, 2;
The first two tables in the join generate all combinations of agents and currencies. We join this to your transactions table and aggregate to get the totals.
I have 2 tables.
table customer have. id , name , age
table order have . id, customer_id , order_amount , order date.
I want to show all name from customer table and sum of order amount from order table according to customer.
customer_id
Name
age
1
Alice
24
2
Bob
52
3
Carol
45
4
Dave
51
order_id
customer_id
order_amount
order_date
1
2
50
2012-4-5
2
1
27
2012-8-1
3
2
12
2013-5-20
4
4
25
2014-1-25
5
4
30
2014-5-30
6
1
20
2014-6-22
EDIT
I tried this but it gives me only bob and sum of all columns instead of separate sum of customers
SELECT customers.name, SUM(orders.order_amount) FROM `orders` INNER JOIN customers WHERE orders.customer_id = customers.customer_id;
Joining condition must be on ON clause, not in WHERE.
You must specify for what group the sum must be calculated.
SELECT customers.name, SUM(orders.order_amount)
FROM `orders`
INNER JOIN customers ON orders.customer_id = customers.customer_id
GROUP BY customers.name;
I am working on a project where users work on reports and enter details of their work in database. My database structure has two tables:
tbl_reports - this table contains all details of work performed
report_id user_id date country status
-----------------------------------------------------------------------
0001 abc 2014-05-04 USA checked
0002 abc 2014-05-04 USA checked
0003 abc 2014-05-05 India checked
0004 lmn 2014-05-04 USA checked
0005 lmn 2014-05-04 India checked
0006 xyz 2014-05-06 Taiwan checked
tbl_time - this table contains all details on time repoted by the users, date and country wise
id user_id date country time (hrs)
----------------------------------------------------
01 abc 2014-05-04 USA 4
02 abc 2014-05-05 India 2
03 lmn 2014-05-04 USA 3
04 lmn 2014-05-04 India 2
05 opq 2014-05-05 Belgium 4
As you can see users "abc and "lmn" have tracked all their tasks appropriately while user "xyz" has not tracked his time yet and user "opq" has tracked his time but has no records of reports he has worked on.
Now out of this I want to extract details of this team GROUPING BY "date" and "country" as below:
date country total_report_count total_time_count
-----------------------------------------------------------------------
2014-05-04 India 1 2
2014-05-04 USA 3 7
2014-05-05 Belgium 0 4
2014-05-05 India 1 2
2014-05-06 Taiwan 1 0
Which means irrespective of which user has tracked his reports or time, I need to generate team report for worked done in which country on which date , its counts and total time tracked.
Now I was able to find total_time_count report using below code:
CREATE VIEW vw_teamreport AS
SELECT
tb1.date , tb1.country,
SUM(tb1.time) AS total_time_count
FROM tbl_time tb1
LEFT JOIN tbl_reports tb2
ON tb2.report_id IS NULL
GROUP BY tb1.date, tb1.country
ORDER BY tb1.date, tb1.country;
Need help to complete the problem, and I am using MYSQL (In case if FULL JOIN is required, FULL JOIN keyword is not supported)
Because there's no FULL JOIN you'll need a query to pull out all the distinct date/country combinations from the UNION of these two tables. Or, you'll need some other query to generate the full list of dates and countries. Call this query A.
You need to write two separate aggregating queries. One will aggregate the hours by date and country and the other will aggregate the reports by date and country. Call these queries B and C.
Then you need to do
SELECT whatever, whatever
FROM (
/*query A*/
) AS a
LEFT JOIN (
/*query B*/
) AS b ON a.date=b.date AND a.country=b.country
LEFT JOIN (
/*query C*/
) AS c ON a.date=c.date AND a.country=c.country
This will produce a correctly summarized report with all the rows you need, and NULLs where there is missing summary data.
Edit
Sorry, forgot about the nested query view restriction. You'll need to create four views, one for each subquery and one for the join query. So it will be:
CREATE VIEW dates_countries AS
SELECT DISTINCT `date`, country FROM tbl_time
UNION
SELECT DISTINCT `date`, country FROM tbl_reports;
CREATE VIEW time_totals AS
SELECT `date`, country, SUM(time) AS tot
FROM tbl_time
GROUP BY `date`, country
CREATE VIEW report_totals AS
SELECT `date`, country, COUNT(*) AS tot
FROM tbl_reports
GROUP BY `date`, country
And finally this view.
CREATE VIEW team_report AS
SELECT a.`date`, a.country,
c.tot AS total_report_count,
b.tot AS total_time_count
FROM dates_countries AS a
LEFT JOIN time_totals AS b ON a.`date` = b.`date` AND a.country = b.country
LEFT JOIN repoorts_totals AS r ON a.`date` = r.`date` AND a.country = r.country;
You don't have much choice about this when you need a view.
you could do a divide it into two sub-queries with each section providing one of the reporting data like this,
CREATE VIEW vw_teamreport AS
SELECT tt.date, tt.country, t1.total_time_count, t2.total_report_count
FROM
(SELECT distinct tb1.date , tb1.country
FROM tbl_time tb1
UNION
SELECT tb1.date , tb1.country
FROM tbl_reports tb1
) tt
LEFT JOIN
(SELECT tb1.date , tb1.country, SUM(tb1.time) AS total_time_count
FROM tbl_time tb1
GROUP BY tb1.date, tb1.country) t1
ON tt.date = t1.date and tt.country = t1.country
LEFT JOIN
(SELECT tb1.date , tb1.country, COUNT(tb1.country) AS total_report_count
FROM tbl_reports tb1
GROUP BY tb1.date, tb1.country)t2
ON tt.date = t2.date and tt.country = t2.country
The first query provides the union for all time & country. The 2nd and the 3rd query provides the report data.
I need to perform a COUNT on a quite a big query, where one of the joined tables has a one-to-many relationship. This is throwing off my result as all data is being multiplied by the number of times an item is repeated in the 'many' side of the one-to-many table.
This is a shortened version of the query showing only the relevant portion to highlight the issue:
SELECT COUNT(trimtype) FROM versiontrim
INNER JOIN trims USING (trim_id)
INNER JOIN prices USING(version_id)
INNER JOIN m_versions USING(version_id)
WHERE trimtype IN('sec', 'help') AND price BETWEEN 200001 AND 210000
GROUP BY version_id
All tables are quite straighforward except m_versions that has the one-to-many relationship and looks like this:
version_id serv_id
1 1
1 2
1 3
1 4
1 5
.... and so on
The expected result of the query is :
version_id COUNT(trimtype)
44 9
54 7
69 9
214 10
216 6
282 1
290 10
Instead I am getting this,ie, all counts multiplied by 5 which is the number of times version_id is repeated in the m_versions table:
version_id COUNT(trimtype)
44 45
54 35
69 45
214 50
216 30
282 5
290 50
How to avoid this behavior?
Thanks
It matches to multiple records on table m_version that is why you are getting invalid result. Try wrapping it a subquery,
INNER JOIN (SELECT DISTINCT version_id FROM m_versions) m USING(version_id)
UPDATE
So the full query will look like this,
SELECT version_id, COUNT(trimtype)
FROM versiontrim
INNER JOIN trims USING (trim_id)
INNER JOIN prices USING(version_id)
INNER JOIN (SELECT DISTINCT version_id FROM m_versions) m USING(version_id)
WHERE trimtype IN('sec', 'help') AND price BETWEEN 200001 AND 210000
GROUP BY version_id
I was successful in writing the query that lists salesmen that did sell to a particular customer, but not those that have not. I suspect it is because the same salesmen that sold to the specific customer, also sold to other customers.
select a.name from salesperson a inner join orders b on
a.salesperson_id = b.salesperson_id where cust_id="4";
I was thinking that modifying the same query like this would do the trick:
.... a.salesperson_id <> b.salesperson_id where cust_id="4";
But the result lists all the salesmen. This is most likely due to the fact that the same salesmen that were returned in the original query, also sold to other customers
The 3 tables look like this:
Salesperson table
salesperson_ID, Name, Age, Salary
1 Abe 61 140000
2 Bob 34 44000
5 Chris 34 40000
7 Dan 41 52000
8 Ken 57 115000
11 Joe 38 38000
Customer table
cust_ID, Name, City Industry Type
4 faralon sacramento H
6 Apple cupertino S
7 Honda NY B
9 Kolb Oshkosh B
Orders table
Number, Order_date, cust_id, salesperson_id, Amount
10 8/2/1996 4 2 540
20 1/30/1999 4 8 1800
30 7/14/1995 9 1 460
40 1/29/1998 7 2 2400
50 2/3/1998 6 7 600
60 3/2/1998 6 7 720
70 5/6/1998 9 7 150
Any help would be greatly appreciated. ~Alpinehyker
You can do something like this:
select a.name from salesperson a
left join orders b on a.salesperson_id = b.salesperson_id and b.cust_id="4"
where b.Number is null
So, get all salepersons, left join to orders for customer 4, and return only rows where there is no such order.
I am assuming that Number is the primary key for Orders, or at least not null.
All salespeople who have NOT sold to customer_ID 4:
SELECT s.Name FROM Salesperson AS s
LEFT JOIN Orders AS o
ON s.salesperson_ID = o.salesperson_ID
WHERE o.customer_ID <> 4
GROUP BY o.salesperson_ID;
Perhaps this will work
SELECT
s.*
FROM `Salesperson` AS s
LEFT JOIN `Orders` AS o ON o.`salesperson_id` = s.`salesperson_ID`
WHERE
o.`cust_id` NOT IN (4)
GROUP BY s.`salesperson_ID`;
Answer to your 2nd question:
SELECT
COUNT(*) AS num_of_orders
,s.`Name`
FROM `Salesperson` AS s
LEFT JOIN `Orders` AS o ON o.`salesperson_id` = s.`salesperson_ID`
GROUP BY s.`salesperson_ID`
HAVING num_of_orders >= 2;
...and 3rd question. (assuming you have your highAchiever table ready)
INSERT INTO `highAchiever`
(`Name`,`Age`)
SELECT
`Name`
,`Age`
FROM `Salesperson`
WHERE
`Salary` >= 100000;