making a query for stock/price trend in mysql - mysql

SQL Fiddle
Table scheme:
CREATE TABLE company
(`company_id` int,`name` varchar(30))
;
INSERT INTO company
(`company_id`,`name`)
VALUES
(1,"Company A"),
(2,"Company B")
;
CREATE TABLE price
(`company_id` int,`price` int,`time` timestamp)
;
INSERT INTO price
(`company_id`,`price`,`time`)
VALUES
(1,50,'2015-02-21 02:34:40'),
(2,60,'2015-02-21 02:35:40'),
(1,70,'2015-02-21 05:34:40'),
(2,120,'2015-02-21 05:35:40'),
(1,150,'2015-02-22 02:34:40'),
(2,130,'2015-02-22 02:35:40'),
(1,170,'2015-02-22 05:34:40'),
(2,190,'2015-02-22 05:35:40')
I'm using Cron Jobs to fetch company prices. In concatenating the price history for each company, how can I make sure that only the last one in each day is included? In this case, I want all of the price records around 05:30am concatenated.
This is the result I'm trying to get (I have used Date(time) to only get the dates from the timestamps):
COMPANY_ID PRICE TIME
1 70|170 2015-02-21|2015-02-22
2 120|190 2015-02-21|2015-02-22
I have tried the following query but it doesn't work. The prices don't correspond to the dates and I don't know how to exclude all of the 2:30 am records before applying the Group_concat function.
SELECT company_id,price,trend_date FROM
(
SELECT company_id, GROUP_CONCAT(price SEPARATOR'|') AS price,
GROUP_CONCAT(trend_date SEPARATOR'|') AS trend_date
FROM
(
SELECT company_id,price,
DATE(time) AS trend_date
FROM price
ORDER BY time ASC
)x1
GROUP BY company_id
)t1
Can anyone show me how to get the desired result?

Ok, so this should work as intended:
SELECT p.company_id,
GROUP_CONCAT(price SEPARATOR '|') as price,
GROUP_CONCAT(PriceDate SEPARATOR '|') as trend_date
FROM price as p
INNER JOIN (SELECT company_id,
DATE(`time`) as PriceDate,
MAX(`time`) as MaxTime
FROM price
GROUP BY company_id,
DATE(`time`)) as t
ON p.company_id = t.company_id
AND p.`time` = t.MaxTime
GROUP BY p.company_id
Here is the modified sqlfiddle.

This is a bit unorthodox but I think it solves your problem:
SELECT company_id,
GROUP_CONCAT(price SEPARATOR'|'),
GROUP_CONCAT(trend_date SEPARATOR'|')
FROM (
SELECT *
FROM (
SELECT company_id,
DATE(`time`) `trend_date`,
price
FROM price
ORDER BY `time` DESC
) AS a
GROUP BY company_id, `trend_date`
) AS b
GROUP BY company_id

Related

order by with union in SQL is not working

Is it possible to order when the data comes from many select and union it together? Such as
In this statement, the vouchers data is not showing in the same sequence as I saved on the database, I also tried it with "ORDER BY v_payments.payment_id ASC" but won't be worked
( SELECT order_id as id, order_date as date, ... , time FROM orders WHERE client_code = '$searchId' AND order_status = 1 AND order_date BETWEEN '$start_date' AND '$end_date' ORDER BY time)
UNION
( SELECT vouchers.voucher_id as id, vouchers.payment_date as date, v_payments.account_name as name, ac_balance as oldBalance, v_payments.debit as debitAmount, v_payments.description as descriptions,
vouchers.v_no as v_no, vouchers.v_type as v_type, v_payments.credit as creditAmount, time, zero as tax, zero as freightAmount FROM vouchers INNER JOIN v_payments
ON vouchers.voucher_id = v_payments.voucher_id WHERE v_payments.client_code = '$searchId' AND voucher_status = 1 AND vouchers.payment_date BETWEEN '$start_date' AND '$end_date' ORDER BY v_payments.payment_id ASC , time )
UNION
( SELECT return_id as id, return_date as date, ... , time FROM w_return WHERE client_code = '$searchId' AND w_return_status = 1 AND return_date BETWEEN '$start_date' AND '$end_date' ORDER BY time)
Wrap the sub-select queries in the union within a SELECT
SELECT id, name
FROM
(
SELECT id, name FROM fruits
UNION
SELECT id, name FROM vegetables
)
foods
ORDER BY name
If you want the order to only apply to one of the sub-selects, use parentheses as you are doing.
Note that depending on your DB, the syntax may differ here. And if that's the case, you may get better help by specifying what DB server (MySQL, SQL Server, etc.) you are using and any error messages that result.
You need to put the ORDER BY at the end of the statement i.e. you are ordering the final resultset after union-ing the 3 intermediate resultsets
To use an ORDER BY or LIMIT clause to sort or limit the entire UNION result, parenthesize the individual SELECT statements and place the ORDER BY or LIMIT after the last one. See link below:
ORDER BY and LIMIT in Unions
(SELECT a FROM t1 WHERE a=10 AND B=1)
UNION
(SELECT a FROM t2 WHERE a=11 AND B=2)
ORDER BY a LIMIT 10;

How to calculate 3 month active users

I have a table that contains an orderId, a timestamp and a customerId, like this:
DROP TABLE IF EXISTS testdata;
CREATE TABLE testdata (
`orderId` int,
`createdOn` datetime(6),
`customerId` int,
PRIMARY KEY (`orderId`)
);
INSERT INTO testdata (orderId, createdOn, customerId) VALUES
('1000001','2020-01-01 17:08:41.460000','101'),
('1000002','2020-01-02 18:01:00.180000','102'),
('1000003','2020-01-03 12:26:02.460000','103'),
('1000004','2020-01-04 13:32:42.610000','104'),
('1000005','2020-01-05 20:21:28.540000','101'),
('1000006','2020-01-06 11:54:20.530000','102'),
('1000007','2020-02-01 20:54:42.470000','102'),
('1000008','2020-02-02 10:21:29.470000','102'),
('1000009','2020-02-03 16:22:23.880000','102'),
('1000010','2020-02-04 16:22:23.880000','103'),
('1000011','2020-02-05 17:08:41.460000','103'),
('1000012','2020-02-06 18:01:00.180000','103'),
('1000013','2020-03-01 12:26:02.460000','102'),
('1000014','2020-03-02 13:32:42.610000','102'),
('1000015','2020-03-03 20:21:28.540000','103'),
('1000016','2020-03-04 11:54:20.530000','103'),
('1000017','2020-03-05 20:54:42.470000','104'),
('1000018','2020-03-06 10:21:29.470000','104'),
('1000019','2020-04-01 16:22:23.880000','103'),
('1000020','2020-04-02 16:22:23.880000','103'),
('1000021','2020-04-03 17:08:41.460000','103'),
('1000022','2020-04-04 18:01:00.180000','104'),
('1000023','2020-04-05 12:26:02.460000','104'),
('1000024','2020-04-06 13:32:42.610000','104'),
('1000025','2020-05-01 20:21:28.540000','103'),
('1000026','2020-05-02 11:54:20.530000','103'),
('1000027','2020-05-03 20:54:42.470000','104'),
('1000028','2020-05-04 10:21:29.470000','104'),
('1000029','2020-05-05 16:22:23.880000','105'),
('1000030','2020-05-06 16:22:23.880000','105'),
('1000031','2020-05-01 20:21:28.540000','104'),
('1000032','2020-05-02 11:54:20.530000','104'),
('1000033','2020-05-03 20:54:42.470000','104'),
('1000034','2020-05-04 10:21:29.470000','105'),
('1000035','2020-05-05 16:22:23.880000','105'),
('1000036','2020-05-06 16:22:23.880000','105')
;
Now I want to calculate for each month the number of customers that have been active (i.e., have an order) within the last 3 months (i.e., current month or the preceding two months).
I manage to calculate the active users for the current month, like this:
SELECT
EXTRACT(YEAR_MONTH FROM createdOn) AS order_createdOn_ym
,COUNT(DISTINCT customerId) AS mau
FROM testdata
GROUP BY order_createdOn_ym
ORDER BY order_createdOn_ym asc
;
(Fiddle over here.)
However, I'm completely stumped as to how you can approach calculating the 3-months-active users.
Any help is greatly appreciated!
Here is one option:
select c.createdmonth, count(distinct customerid) as mau
from (
select distinct date_format(createdon, '%Y-%m-01') as createdmonth
from testdata
) c
left join testdata t
on t.createdon >= c.createdmonth - interval 2 month
and t.createdon < c.createdmonth + interval 1 month
group by c.createdmonth
The idea is to enumerate the distinct months, then bring the table with a left join that recovers the last 2 month and the current month. You can then aggregate and count the number of distinct customers per group.
Thanks to #GMB for providing the solution. Purely as a matter of taste I prefer to have the month interval the following way though:
SELECT date_format(c.end_of_createdOn_month, '%Y-%m') as order_month,
count(distinct customerid) as mau_3m
FROM (
select distinct LAST_DAY(createdOn) as end_of_createdOn_month
from testdata
) c
LEFT JOIN testdata t
ON t.createdon >= (c.end_of_createdOn_month - interval 3 month)
AND t.createdon <= c.end_of_createdOn_month
GROUP BY c.end_of_createdOn_month;

SQL Group by returning wrong results

I have a salary table in which I am trying to return determine the lowest salary earned and by which industry for each year however despite getting the correct lowest salary earned I am receiving the wrong industry name.
I am aware that it is due to the fact that I have utilized GROUP BY without placing a constraint(?) on it hence it is returning me the wrong value but I am not sure how I can solve it.
SALARY TABLE
salaryID
salaryAmount
salaryYear
industryName (ForeignKey)
Can someone please guide me on the right path?
**(Problem Code)**
SELECT MIN(S.salary), S.industryName, S.salaryYear
FROM salary
GROUP BY S.salaryYear;
**(Attempted solution)**
SELECT S.salary
FROM salary
INNER JOIN
SELECT (min(S1.amount)), S1.year, S1.industryName, S1.salaryId
FROM salary S1
GROUP BY S1.year
ON S.salaryId = S1.salaryId);
Use a proper GROUP BY. Any non-aggregated columns must be included in GROUP BY.
SELECT MIN(amount), year
FROM salary
GROUP BY year
If you want to include industryName,
SELECT amount, year, industryName, salaryId
FROM (
SELECT amount, year, industryName, salaryId
, ROW_NUMBER() OVER(PARTITION BY year ORDER BY amount) AS rn
FROM salary
) a
WHERE rn = 1
Pre-MySQL 8 version
SELECT *
FROM salary s
INNER JOIN (
SELECT MIN(amount) AS minAmount, year
FROM salary
GROUP BY year
) m ON m.minAmount = s.amount AND m.year = s.year
I think you need a self-join :
SELECT s1.industryName, s2.min_salary, s2.salaryYear
FROM salary s1
JOIN
(
SELECT MIN(salary) as min_salary, salaryYear
FROM salary
GROUP BY salaryYear
) s2
ON s1.salary = s2.min_salary
AND s1.salaryYear = s2.salaryYear;
The Demo of this query with your sample data

How to get the ID of the max counted user in Order table

I have a table with
orderNumber(pk) , customerNumber , comment
I have to count the maximum order placed by a user and show its user ID and MAX count . I have following Query
It shows the count Right but it takes the first CustomerNumber in the table
SELECT maxCount.customerNumber , MAX(`counted`) FROM
(
SELECT customerNumber, COUNT(*) AS `counted`
FROM `orders`
GROUP BY `customerNumber`
)as maxCount
Thanks & regards
Just use ORDER BY with your inner query:
SELECT customerNumber, COUNT(*) AS `counted`
FROM `orders`
GROUP BY `customerNumber`
ORDER BY COUNT(*) DESC
LIMIT 1
If you want to return all customer numbers in the event of a tie, you can use a HAVING clause with a subquery which identifies the maximum count:
SELECT customerNumber, COUNT(*) AS counted
FROM orders
GROUP BY customerNumber
HAVING COUNT(*) = (SELECT MAX(t.counted) FROM (SELECT COUNT(*) AS counted
FROM orders
GROUP BY customerNumber) t)
Demo here:
SQLFiddle

Balancing out MYSQL select statements

I inserted 'vanity_name' and 'name' into the first and second SELECT statements respectively.
I get a mismatched number of columns error, which I'm confused about because I added a column to both select statements to maintain a balance.
SQL Statement:
SELECT id,
vanity_name,
Date_format(DATE, '%M %e, %Y') AS DATE,
TYPE
FROM (SELECT resume_id AS id,
date_mod AS DATE,
'resume' AS TYPE
FROM resumes
WHERE user_id = '1'
UNION ALL
SELECT profile_id,
name,
date_mod AS DATE,
'profile'
FROM profiles
WHERE user_id = '1'
ORDER BY DATE DESC
LIMIT
5) AS d1
ORDER BY DATE DESC
Erm, you have four columns in your outer select, three in the inner select.
id, vanity_name, date, type
vs.
id, date, TYPE
Based on the parenthesis, you're trying to union:
(SELECT resume_id AS id, date_mod AS date, 'resume' AS TYPE FROM resumes WHERE user_id = '1'
with
SELECT profile_id,name,date_mod AS date, 'profile' FROM profiles ... LIMIT 5)
and they obviously don't match. Reposition your parens.