I am trying to merge two queries into one, but UNION is not working for me.
Here is the code:
SELECT
Customer_A,
Activity,
Customer_P,
Purchase
FROM (
SELECT
buyer_id as Customer_A,
COUNT(buyer_id) As Activity
FROM
customer_info_mxs
GROUP BY buyer_id
UNION ALL
SELECT
buyer_id as Customer_P,
SUM(purchase_amount) As Purchase
FROM
customer_info_mxs
GROUP BY buyer_id
)sub
I expect to have 4 columns as a result, but I get 2 instead (Customer_A) and(Activity).
If the query is supposed to return a list of customers, their number of purchases, and the total amount they’ve spent, then you can use a single query like this:
SELECT mxs.buyer_id as Customer,
COUNT(mxs.purchase_id) As Activity,
SUM(mxs.purchase_amount) As Purchases
FROM customer_info_mxs mxs
GROUP BY mxs.buyer_id;
Otherwise, your first subquery will always be a buyer_id and a value of 1.
Be sure to change purchase_id to whatever the unique id is for each purchase if you wish to see that number.
I think there is some confusion about the union statement. The union statement returns a row set that is the sum of all of the 'unioned' queries; since these queries have only 2 columns, the combined output only has two columns. The fact that the columns have different names is irrelevant. The column names in the output are being applied from the first query of the union.
One option is to just do
select buyer_id, count(buyer_id), sum(purchase_amount) from customer_info_mxs group by buyer_id
From your question, it looks like you are trying to do a pivot, turning some of the rows into additional columns. That could be done with ... some difficulty.
i read your comment,
'main goal is to creat a dataset in which returns 5 columns as: Customer_A, Activity (top 100), customer_P, Purchase(top 100), inner join of activity and purchase'
please try this query
SET #row_number = 0, #row_number2 = 0;
SELECT t1.Customer_A,t1.Activity, t2.Customer_P, t2.Purchase
from (
SELECT (#row_number:=#row_number + 1) AS n, t.Customer_a, t.Activity
from (
select buyer_id as Customer_A,COUNT(buyer_id) As Activity
FROM customer_info_mxs
GROUP BY buyer_id
order by Activity desc
Limit 100
)t
) t1
left join (
SELECT (#row_number2:=#row_number2 + 1) AS n,
FROM (
select buyer_id as Customer_P, SUM(purchase_amount) Purchase
FROM customer_info_mxs
GROUP BY buyer_id
order by Purchase desc
Limit 100
)t
) t2 on t2.n=t1.n
basic idea is, i just create some temporary number 0-99 to table 1 (t1) and join to temporary number on table 2 (t2)
Related
"loyal" customers are considered loyal if they have purchased at least 5 times.
I am trying to build an SQL query which returns only "loyal" customers along with the day on
which they become "loyal" customers (the day of their 5th transaction).
user_id
purchase_ts
f594fsae
2021-07-21
........
............
Ideally the desired output would be as follows
loyal_user_id
Loyal_Moment
f594fsae
2021-07-29
..............
............
I tried creating a new table as follows:
SELECT user_id, purchase_ts
FROM Customers
WHERE user_id IN (
SELECT user_id
FROM Customers
GROUP BY user_id
HAVING COUNT (user_id) >=5
)
But I am having trouble, any suggestions?
On MySQL 8 you could use:
with cte as
( select *,
row_number() over (partition by user_id order by purchase_ts asc) row_num
from accounts
)
select user_id,purchase_ts
from cte
where row_num >=5;
Result:
user_id purchase_ts
f594fsae 2021-07-25
f632fsae 2021-07-25
Demo
Since MySQL introduced the support of subquery a long time ago, we have been using its techniques in some MySQL-version-nonspecific scenarios. In this case, we can use a correlated subquery to get exactly the fifth purchase_ts by using the LIMIT [OFFSET] clause. The WHERE clause is used to exclude those purchase_id which doesn't have a fifth purchase_ts.
select distinct user_Id,
(select purchase_ts from purchase where user_id=p.user_id order by purchase_ts limit 4,1) as loyal_time
from purchase p
where (select purchase_ts from purchase where user_id=p.user_id order by purchase_ts limit 4,1) is not null;
I have a mysql query that I'm trying to figure out.
Basically I have table 1 cols: estate agent, price, location, bungalow, cottage
and I have table 2 cols: estate agent, price, location, penthouse, duplex
As you can see these tables are very different.
I need a query to select all cols from table 1 or 2 depending on which has the highest price. For example:
SELECT * FROM table1, table2 WHERE table1.price = table2.price ORDER BY price DESC LIMIT 1,1;
If the tables are so similar, you could UNION them, then sort the result descending and get the higher price.
(SELECT * FROM table1)
UNION ALL
(SELECT * FROM table2)
ORDER BY price DESC
LIMIT 1
You would need to specify the columns explicitly if you want to give them aliases.
Re your followup question:
If you have a few columns different between the two tables, and you want to preserve them, you need to move away from SELECT * and name all the columns explicitly.
(SELECT estate agent, price, location, bungalow, cottage, NULL AS penthouse, NULL AS duplex
FROM table1)
UNION ALL
(SELECT estate agent, price, location, NULL, NULL, penthouse, duplex
FROM table2)
ORDER BY price DESC
LIMIT 1
You don't need to give aliases in the second subquery because column names are always determined by the first query of a UNION. Even if you do declare column aliases in the second query, they'll be ignored.
try this:
SELECT * FROM (
SELECT * FROM table1
UNION
SELECT * FROM table2
ORDER BY price desc)x
LIMIT 1;
In my SQL I am getting transactions relating to a user and a business. However, I also need to get the name of the business. It is found in column business_name under table Businesses. In my example SQL, I would want to get the business name for business_id=1. My current code works aside from not getting the business name.
(SELECT TRUNCATE(code_reward_amount, 2) AS amount, UNIX_TIMESTAMP(code_redeemed_date) AS date, 0 AS action_number
FROM CodesRedeemed
WHERE code_redeemed_by_user_id=191 AND code_business_id=1)
UNION ALL
(SELECT TRUNCATE(action_amount, 2) AS amount, UNIX_TIMESTAMP(action_date) AS date, action_number
FROM BusinessAccountActions
WHERE action_user_id=191 AND action_business_id=1)
ORDER BY date DESC
LIMIT 100
In my second code attempt, it does get the business name, however, it is not efficient to do the select in every row since the business name would be the same for each row. How can I do it once and apply it to each row? Perhaps somewhere outside of the UNION ALL? Here is my working code, however, I would like to optimize it so it doesn't SELECT from Businesses for the business_name in every single row (since the business_name is guaranteed to be the same for all rows since they share the same business_id).
(SELECT TRUNCATE(code_reward_amount, 2) AS amount, UNIX_TIMESTAMP(code_redeemed_date) AS date, 0 AS action_number, (SELECT business_name FROM Businesses WHERE business_id=1) AS business_name
FROM CodesRedeemed
WHERE code_redeemed_by_user_id=191 AND code_business_id=1)
UNION ALL
(SELECT TRUNCATE(action_amount, 2) AS amount, UNIX_TIMESTAMP(action_date) AS date, action_number, (SELECT business_name FROM Businesses WHERE business_id=1) AS business_name
FROM BusinessAccountActions
WHERE action_user_id=191 AND action_business_id=1)
ORDER BY date DESC
LIMIT 100
business_id would change depending on the business. I am just testing it for business_id 1 right now. How would I optimize (mainly not checking for business_name in every single row)? Thank you.
Use a JOIN.
SELECT u.amount, u.date, b.business_name, u.action_number
FROM (
(SELECT TRUNCATE(code_reward_amount, 2) AS amount, UNIX_TIMESTAMP(code_redeemed_date) AS date, 0 AS action_number
FROM CodesRedeemed
WHERE code_redeemed_by_user_id=191 AND code_business_id=1)
UNION ALL
(SELECT TRUNCATE(action_amount, 2) AS amount, UNIX_TIMESTAMP(action_date) AS date, action_number
FROM BusinessAccountActions
WHERE action_user_id=191 AND action_business_id=1)
ORDER BY date DESC
LIMIT 100) AS u
CROSS JOIN Businesses AS b
WHERE b.business_id = 1
Using a JOIN as Bamar suggested is a perfectly acceptable way to do it, and is how I would most likely do it.
However, you could use a user defined variable and replace that additional select with that.
SELECT business_name FROM Businesses WHERE business_id=1 LIMIT 1 INTO #bname;
(SELECT TRUNCATE(code_reward_amount, 2) AS amount, UNIX_TIMESTAMP(code_redeemed_date) AS date, 0 AS action_number, (SELECT business_name FROM Businesses WHERE business_id=1) AS business_name
FROM CodesRedeemed
WHERE code_redeemed_by_user_id=191 AND code_business_id=1)
UNION ALL
(SELECT TRUNCATE(action_amount, 2) AS amount, UNIX_TIMESTAMP(action_date) AS date, action_number, #bname AS business_name
FROM BusinessAccountActions
WHERE action_user_id=191 AND action_business_id=1)
ORDER BY date DESC
LIMIT 100
Write a query to display the customer name who visited the second highest number of times
select customer_id,count(*) from booking group by customer_id ;
using this query i got the count of number of visits for each customer as shown below
CUSTOMER_ID,COUNT(*)
C001,6
C002,1
C003,1
C004,1
C005,4
but i want to display only c005 since he has visited the second maximum time
SELECT customer_id, COUNT(*)
FROM booking
GROUP BY customer_id
HAVING COUNT(*) <> (SELECT MAX(t.custCount)
FROM (SELECT COUNT(*) AS custCount
FROM booking
GROUP BY customer_id) t )
ORDER BY COUNT(*) DESC
LIMIT 1
As a side note, this won't work if there are ties for second place. In this case, you use the above query as a condition in the WHERE clause, e.g.
SELECT customer_id
FROM booking
GROUP BY customer_id
HAVING COUNT(*) = (query given above)
You can use a outer query and filter the same like
select customer_id from (
select customer_id,
count(*) as datacount
from booking
group by customer_id ) xxx
order by datacount desc
limit 1;
I use similar queries (10) as following queries (modified) to find sum
SELECT sum(amount) AS amount
FROM `students`
WHERE sex='M'
&& name in ('salil', 'anil', 'gaikwad')
...and:
SELECT sum(amount) AS amount
FROM `students`
WHERE sex='M'
&& name in ('salil1', 'anil1', 'gaikwad1')
i want to make a single query of the above 10 queries. is it possible?
You can use UNION
SELECT 'subset1', sum(amount) AS amount FROM students WHERE sex='M' and name in ('salil', 'anil', 'gaikwad')
UNION
SELECT 'subset2', sum(amount) AS amount FROM students WHERE sex='M' and name in ('salil1', 'anil1', 'gaikwad1')
However, you probably query these sets of students for a reason, perhaps anil, salil and gaikwad are one group of students. If so, you should reflect this in the database structure, not in your code.
You could add a field 'SUbset' or 'Group' or whatever that is, to students table, so it looks like this:
name group_id
salil 1
anil 1
gaikwad 1
salil1 2
...
Then you can do
select group_id, sum(amount) from students group by group_id
Try something like this
SELECT sum(amount) AS amount
FROM students INNER JOIN
(SELECT 'salil%' Val UNION SELECT 'anil%' UNION SELECT 'gaikwad%') s ON students.NAME LIKE s.Val
WHERE sex='M'
This allows you to use the values in the second Table to join with LIKE.