Is there a more effective way to return multiple columns from a table that contains a date column instead of using inline subqueries?
SELECT (SELECT SUM(`value`) FROM `data` WHERE MONTH(`date`) = 1 AS `Jan`),
(SELECT ...) // Feb, Mar, etc.
Because having 12 inline subqueries is taxing on the query engine, right?
SELECT YEAR(`date`) as `YEAR`,
SUM(CASE WHEN MONTH(`date`)=1 THEN `value` ELSE 0 END) AS `JAN`,
...
GROUP BY YEAR(`date`)
SELECT SUM(`value`) FROM `data` GROUP BY MONTH(`date`)
Related
I'm new to SQL and trying to calculate YoY Sales over different stores with quarterly granularity. Table is as follows
So far I have:
SELECT Store_number, SUM(Sales) AS Sales_q1_2018
FROM table1
WHERE Sale_date BETWEEN '2018-01-01' AND '2018-03-31'
GROUP BY Store_number
ORDER BY Sales_Q1_2018
I need to do add a column with the following calculation: (sum(sales q1 2018) - sum(sales q1 2017)) / sum(sales q1 2017)
How can I set different date parameters for a temporary calculation? Thanks
You can use a subquery for retrive the 2017 q1 join these to you actual query
SELECT Store_number, SUM(Sales) AS Sales_q1_2018, (SUM(Sales) - Sales_q1_2107)/Sales_q1_2107
FROM table1
INNER JOIN (
SELECT Store_number, SUM(Sales) AS Sales_q1_2107
FROM table1
WHERE Sale_date BETWEEN '2017-01-01' AND '2017-03-31'
GROUP BY Store_number
) t2 t2.Store_number = table1.Store_number
WHERE Sale_date BETWEEN '2018-01-01' AND '2018-03-31'
GROUP BY Store_number
Try this:
SELECT Store_number,
((SUM(IF(year(Sale_date)='2018',Sales,0))
-SUM(IF(year(Sale_date)='2017',Sales,0)))
/SUM(IF(year(Sale_date)='2017',Sales,0))) Q1_2018_vs_2017
FROM table1
WHERE QUARTER(Sale_date)=1 AND YEAR(Sale_date) IN ('2017','2018')
GROUP BY Store_number;
DEMO ON SQL FIDDLE
Assuming your quarters are calendar quarters, I would write the query as:
SELECT Store_number,
SUM(CASE WHEN YEAR(Sale_date) = 2017 THEN Sales ELSE 0 END) AS Sales_q1_2018,
SUM(CASE WHEN YEAR(Sale_date) = 2018 THEN Sales ELSE 0 END) AS Sales_q1_2017,
(SUM(CASE WHEN YEAR(Sale_date) = 2018 THEN Sales ELSE - Sales END) /
SUM(CASE WHEN YEAR(Sale_date) = 2017 THEN Sales END)
) as calculation
FROM table1
WHERE YEAR(Sale_date) IN (2017, 2018) AND
MONTH(Sale_date) IN (1, 2, 3)
GROUP BY Store_number
ORDER BY Sales_Q1_2018;
Here is the SQL Fiddle.
This is similar to #cdaiga's answer, but with the following important differences:
Functions such as YEAR() return numbers, so the comparisons are to numbers, not strings.
CASE expressions are the ANSI-standard way of including conditional logic in a query. IF() is MySQL-specific.
The ratio protects against division by 0.
This is the table, I need to get count of rows for status cloesd and status unclosed for every month in a year. Thanks in advance.
The table image
You can try following query.
SELECT *
FROM
(
(
SELECT MONTH(mt1.calltime) AS MONTH, YEAR(mt1.`calltime`) AS YEAR, COUNT(mt1.`status`) AS closed, 0 AS unclosed
FROM myTable mt1
WHERE mt1.`status`='closed'
)
UNION ALL
(
SELECT MONTH(mt2.calltime) AS MONTH, YEAR(mt2.`calltime`) AS YEAR, 0 AS closed, COUNT(mt2.`status`) AS unclosed
FROM myTable mt2
WHERE mt2.`status`='unclosed'
)
) AS tablea
GROUP BY tablea.month
Replace myTable with your tablename.
MONTH will extract month of the year from the given date, and YEAR will give year.
SQL FIDDLE
select count(*) from table1
where status in ('closed,unclosed')
group by status,month(call_time)
Try this:
SELECT COUNT(*) FROM <table name> WHERE status="closed";
SELECT COUNT(*) FROM <table name> WHERE status="unclosed";
Get year part from the date using YEAR and month part using MONTH functions and status column from the table and use this as a subset and then use CASE expression to count the Closed and Unclosed status group by the year and month.
Query
SELECT t.`YEAR`, t.`MONTH`,
SUM(CASE t.`status` WHEN 'Closed' THEN 1 ELSE 0 END) AS `Closed`,
SUM(CASE t.`status` WHEN 'Unclosed' THEN 1 ELSE 0 END) AS `UnClosed`
FROM(
SELECT YEAR(`call_time`) AS `YEAR`,
MONTH(`call_time`) AS `MONTH`,
`status`
FROM `your_table_name`
)t
GROUP BY t.`YEAR`, t.`MONTH`;
SQL Fiddle demo
I am trying to get the total sum of a column and the sum of the same column between 2 dates in one query. is this possible?
My table looks like this:
uid|amount|date
The two queries i am trying to make one of:
SELECT sum(amount) as `keys` FROM tbl_keys WHERE uid = 1
SELECT sum(amount) as `keys` FROM tbl_keys WHERE uid = 1 AND YEAR(`date`) = YEAR(CURRENT_DATE)
AND MONTH(`date`) = MONTH(CURRENT_DATE)
You could use a UNION query:
SELECT 'All' AS cnt, sum(amount) as `keys` FROM tbl_keys WHERE uid = 1
UNION ALL
SELECT 'Current_month' AS cnt, sum(amount) as `keys`
FROM tbl_keys
WHERE
uid = 1
AND `date`<= last_day(current_date)
`date`>= current_date - interval (day(current_date)-1) day
(I prefer to use >= and <= on the date column, as it can make use of an index if present, while functions like MONTH() or YEAR() cannot, also I assume that date is a date columnd and that it doesn't contain time informations).
If you want the result in one row, you could use an inline query:
SELECT
(SELECT sum(amount) as `keys` FROM tbl_keys WHERE uid = 1) AS total,
(SELECT sum(amount) as `keys`
FROM tbl_keys
WHERE
uid = 1
AND `date`<= last_day(current_date)
`date`>= current_date - interval (day(current_date)-1) day
) AS current_month
Something like this:
SELECT sum(amount) as `keys`,
(
SELECT sum(t.amount)
FROM tbl_keys as t
WHERE t.uid = tbl_keys.uid AND YEAR(t.`date`) = YEAR(CURRENT_DATE)
AND MONTH(t.`date`) = MONTH(CURRENT_DATE)
) as `keys2`
FROM tbl_keys
WHERE uid = 1
SELECT sum(amount) AS `keys`
FROM (
SELECT amount FROM tbl_keys
UNION ALL
SELECT amount FROM tbl_keys
WHERE uid = 1
AND YEAR(`date`) = YEAR(CURRENT_DATE)
AND MONTH(`date`) = MONTH(CURRENT_DATE)
) AS new_table;
Using a UNION clause, you will get the desired output you want.
Use CASE to count only the amount for the specified date:
SELECT SUM(amount) AS `keys`,
SUM(CASE WHEN YEAR(`date`) = YEAR(CURRENT_DATE)
AND MONTH(`date`) = MONTH(CURRENT_DATE) THEN amount ELSE 0 END) AS 'keys2'
FROM tbl_keys
WHERE uid = 1
;
My guess is that this will run more efficient than a solution using UNION SELECT.
How can these SQL-queries to extract statistics from my database be combined for better performance?
$total= mysql_query("SELECT COUNT(*) as number, SUM(order_total) as sum FROM history");
$month = mysql_query("SELECT COUNT(*) as number, SUM(order_total) as sum FROM history WHERE date >= UNIX_TIMESTAMP(DATE_ADD(CURDATE(),INTERVAL -30 DAY))");
$day = mysql_query("SELECT COUNT(*) as number, SUM(order_total) as sum FROM history WHERE date >= UNIX_TIMESTAMP(CURDATE())");
If you want to all the data in a single query, you have two choices:
Use a UNION query (as sugested by bishop in his answer)
Tweak a query to get what you need in a single row
I'll show option 2 (option 1 has been already covered).
Note: I'm using user variables (that stuff in the init subquery) to avoid writing the expressions again and again. Also, to filter the aggregate data, I'm using case ... end expressions.
select
-- Your first query:
count(*) as number, sum(order_total) as `sum`
-- Your second query:
, sum(case when `date` <= #prev_date then 1 else 0 end) as number_prev
, sum(case when `date` <= #prev_date then order_total else 0 end) as sum_prev
-- Your third query:
, sum(case when `date` <= #cur_date then 1 else 0 end) as number_cur
, sum(case when `date` <= #cur_date then order_total else 0 end) as sum_cur
from (
select #cur_date := unix_timestamp(curdate())
, #prev_date := unix_timestamp(date_add(curdate(), interval -30 day))
) as init
, history;
Hope this helps
Since the queries have the same column structure, you can ask MySQL to combine them with the UNION operation:
(SELECT 'total' AS kind, COUNT(*) as number, SUM(order_total) as sum FROM history~
UNION
(SELECT 'by-month' AS kind, COUNT(*) as number, SUM(order_total) as sum FROM history WHERE date <= UNIX_TIMESTAMP(DATE_ADD(CURDATE(),INTERVAL -30 DAY)))
UNION
(SELECT 'by-day' AS kind, COUNT(*) as number, SUM(order_total) as sum FROM history WHERE date <= UNIX_TIMESTAMP(CURDATE()))
I'm sure this is a fairly trivial problem, but I'm not sure what to google to find the solution.
I have a table that looks like this:
CREATE TABLE IF NOT EXISTS `transactions` (
`name` text collate utf8_swedish_ci NOT NULL,
`value` decimal(65,2) NOT NULL,
`date` date NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_swedish_ci ROW_FORMAT=COMPACT;
I populate this by cutting and pasting data from my internet banking service.
Value can be a negative or positive value, what both date and name contain should be fairly obvious ;)
I have constructed a query to let me see my bottom line for each month:
SELECT sum(`value`) as 'change', DATE_FORMAT(`date`, '%M %Y') as 'month'
FROM `transactions`
WHERE 1
GROUP BY year(`date`), month(`date`)
Now I would like to add the total accumulated money in the account at the end of the month as an additional column.
SELECT sum(`value`) as 'change', DATE_FORMAT(`date`, '%M %Y') as 'month',
(SELECT sum(`value`) FROM `transactions` WHERE `date` <= 123) as 'accumulated'
FROM `transactions`
WHERE 1
GROUP BY year(`date`), month(`date`)
123 is not exactly what I want in there, but I do not understand how to get at the result from my DATE_FORMAT inside that subquery.
Is this even the proper way to approach the problem?
This is mostly a personal exercise (running on a very small dataset) so I'm not very concerned about performance, readable SQL is far more important.
I am running a InnoDB table on MySQL 5.0.45
SELECT change,
CONCAT(mymonth, ' ', myyear) AS 'month',
(
SELECT SUM(`value`)
FROM `transactions`
WHERE `date` < DATE_ADD(STR_TO_DATE(CONCAT('01.', mymonth, '.', myyear, '%D.%M.%Y'), INTERVAL 1 MONTH))
)
FROM (
SELECT sum(`value`) as 'change', YEAR(date) AS myyear, MONTH(date) AS mymonth
FROM `transactions`
WHERE 1
GROUP BY
YEAR(`date`), MONTH(`date`)
) q
You wrote that you don't cate for performance, but this syntax is not much more complex but will be more efficient (just in case):
SELECT SUM(value) AS change,
CONCAT(MONTH(`date`), ' ', YEAR(`date`)) AS 'month',
#r : = #r + SUM(value) AS cumulative
FROM (
SELECT #r := 0
) AS vars,
transactions
WHERE 1
GROUP BY
YEAR(`date`), MONTH(`date`)
ORDER BY
YEAR(`date`), MONTH(`date`)
This one will count cumulative SUM's as well, but it will count each month only once.