I have two tables with given structure :
Table 1:
ST_Id ST_Name
1 xx
2 yy
Table 2 :
AT_Id AT_Amt ST_ID Date
1 500 1 2015-11-17
2 1000 1 2015-11-15
3 300 1 2015-12-1
4 200 2 2015-11-2
I want to get results from mysql order by month. In my php page, I have a table structure for displaying data like shown below
State December November September
xx 300 1500 0
yy 0 200 0
Along with these months, I have a sorting link for sorting the amounts based on month. So when I click the sorting image corresponding to the month November, it should list the table data according to the order of amount in that column. If I clicked for ascending sorting it should list like,
State December November September
yy 0 200 0
xx 300 1500 0
I tried with following query :
SELECT ST.ST_Name,SUM(AT.AT_Amt)
FROM `Table2` AS AT
LEFT JOIN Table1 AS ST ON AT.ST_Id = ST.ST_Id
WHERE AT.Date BETWEEN '2015-04-01' AND '2015-12-31' GROUP BY MONTH( AT.Date) ORDER BY IF(MONTH(AT.Date) = 11 , SUM(AT.AT_Amt) , MONTH( AT.Date)) ASC
This query is returning data like shown below.
ST_Name SUM(AT.AT_Amt)
xx 300
xx 1700
But the expected result is :
ST_Name SUM(AT.AT_Amt)
yy 200
xx 1500
Can anyone please help me fix this? Thanks in advance.
You can do something like this:
SELECT
st_name,
SUM(CASE WHEN MONTH(dt) = 12 THEN at_amt ELSE 0 END) AS December,
SUM(CASE WHEN MONTH(dt) = 11 THEN at_amt ELSE 0 END) AS November,
SUM(CASE WHEN MONTH(dt) = 10 THEN at_amt ELSE 0 END) AS September
FROM table2
INNER JOIN table1 ON table1.st_id = table2.st_id
GROUP BY st_name;
Once you get data into PHP, you could just use JavaScript to do client-side sorting using TableSorter or similar library. That way sorting doesn't incur the expense of re-querying the database.
Result of your data will be:
+---------+----------+----------+-----------+
| st_name | December | November | September |
+---------+----------+----------+-----------+
| xx | 300 | 1500 | 0 |
| yy | 0 | 200 | 0 |
+---------+----------+----------+-----------+
Click the link below for a running demo:
SQLFiddle
EDIT:
Based on your comment, is this something you need?
SELECT
ST.ST_Name,
SUM(AT.AT_Amt) as Amount
FROM `Table2` AS AT
LEFT JOIN Table1 AS ST ON AT.ST_Id = ST.ST_Id
WHERE AT.dt BETWEEN '2015-04-01' AND '2015-12-31'
AND MONTH(AT.dt) = 11
GROUP BY ST.ST_Name
ORDER BY Amount
Example: http://sqlfiddle.com/#!9/51c7c/7 for demo
Result:
| st_name | Amount |
|---------|--------|
| yy | 200 |
| xx | 1500 |
Related
This is sample data in my table
id_item | qty | t_in | t_out | created_at
1 5 1 0 2018-07-05 10:41:00
1 5 1 0 2018-08-03 10:41:00
1 5 0 1 2018-08-05 10:41:00
1 5 1 0 2018-09-05 10:41:00
1 5 1 0 2018-09-20 10:41:00
1 5 0 1 2018-10-31 10:41:00
My expected result will be
id_item | qty | year | month
1 5 2018 07
1 5 2018 08
1 15 2018 09
1 10 2018 10
What i have tried it works, but not desired output when want to group by montly
$date = '2018-10-31'
$test = Model::whereDate('created_at','<=',$date)->select(DB::raw('(SUM(CASE T_IN WHEN 1 THEN qty ELSE qty * - 1 END)) as total'))->groupBy('id_item')->get();
Raw queries to get the quantity for one month
Select id_item,
(SUM(CASE T_IN WHEN 1 THEN qty ELSE qty * - 1 END)) as total
from transactions
where DATE(created_at) <= 2018-10-31
group by id_item
Worst case
$last_day_of_month = [//list of last day of each month]
//then using loop to get qty of each month refer to the raw queries above
From the query above, i only able to get one line of record. I also tried to group by month and year but incorrect result caused of the date condition. How can i include multiple <= $date condition and group it accordingly to get desired output?
Any idea or is that possible to make it? Thanks.
It is a Rolling Sum problem. In newer versions of Mariadb/MySQL, it can be solved using Window Functions with Frames. However, you don't have that available.
We can rather solve this using user-defined variables. In a Derived table, we first determine the total change in qty for a month. Then, we use this result-set to calculate "final qty" at the end of a month, by adding up the previous month (row)'s qty with current month (row)'s qty_change.
I have also extended the query to consider the cases when there are more than one id_item values.
Try the following Raw query:
SELECT
#roll_qty := CASE WHEN #id_itm = dt.id_item
THEN #roll_qty + dt.qty_change
ELSE dt.qty_change
END AS qty,
#id_itm := dt.id_item AS id_item,
dt.year,
dt.month
FROM
(
SELECT
t.id_item,
SUM(t.qty * t.t_in - t.qty * t.t_out) AS qty_change,
YEAR(t.created_at) AS `year`,
LPAD(MONTH(t.created_at), 2, '0') AS `month`
FROM your_table AS t
GROUP BY t.id_item, `year`, `month`
ORDER BY t.id_item, `year`, `month`
) AS dt
CROSS JOIN (SELECT #roll_qty := 0,
#id_itm := 0
) AS user_init_vars;
| id_item | year | month | qty |
| ------- | ---- | ----- | --- |
| 1 | 2018 | 07 | 5 |
| 1 | 2018 | 08 | 5 |
| 1 | 2018 | 09 | 15 |
| 1 | 2018 | 10 | 10 |
View on DB Fiddle
If you are going to use variables, you need to do it correctly. MySQL does not guarantee the order of evaluation of expressions in a SELECT. So, a variable should not be assigned in one expression and then used in another.
This makes for complicated expressions, but it is possible:
select yyyy, mm, total,
(#t := if(#ym = concat_ws('-', yyyy, mm), #t + total,
#ym := concat_ws('-', yyyy, mm), total
)
) as running_total
from (select year(created_at) as yyyy, month(created_at) as mm,
id_item,
sum(case T_IN when 1 then qty else - qty end) as total
from transactions
where created_at < '2018-11-01'
group by id_item
order by id_item, min(created_at)
) i cross join
(select #ym := '', #n := 0);
How to count records base on a specific Year and Month? Eg, that I want to make is as below,
| Category | Total |
|-----------|--------|
| Cat 1 | 15 |
| Cat 2 | 0 |
| Cat 3 | 20 |
| Cat 4 | 18 |
This is the query that I did,
SELECT kc.jenis_katCuti, count(pc.id) AS jum_cuti
FROM permohonan_cuti pc
RIGHT JOIN kat_cuti kc on kc.katCuti_id=pc.katCuti_id
WHERE YEAR(pc.tarikh_mula)=2017 AND MONTH(pc.tarikh_mula)=1
GROUP BY kc.jenis_katCuti
From the query, it will display only for the Year and Month that user choose. I want it to display all the records and return 0 if there is no record for the particular month and year.
I also tried below but still with no success,
SELECT kc.jenis_katCuti, count(YEAR(pc.tarikh_mula)=2017
AND MONTH(pc.tarikh_mula)=1) AS jum_cuti
FROM permohonan_cuti pc
RIGHT JOIN kat_cuti kc on kc.katCuti_id=pc.katCuti_id
GROUP BY kc.jenis_katCuti
Use case when in count like this:
SELECT
kc.jenis_katCuti,
count(CASE WHEN YEAR(pc.tarikh_mula) = 2017 AND MONTH(pc.tarikh_mula) = 1 THEN 1 ELSE NULL END) AS jum_cuti
FROM permohonan_cuti pc
RIGHT JOIN kat_cuti kc on kc.katCuti_id = pc.katCuti_id
GROUP BY kc.jenis_katCuti
cause count only excludes null, the expression YEAR(pc.tarikh_mula) = 2017 AND MONTH(pc.tarikh_mula) = 1 will return 1 or 0, count will include both of them.
If you just use YEAR(pc.tarikh_mula) = 2017 AND MONTH(pc.tarikh_mula) = 1, try sum:
SELECT
kc.jenis_katCuti,
sum(YEAR(pc.tarikh_mula) = 2017 AND MONTH(pc.tarikh_mula) = 1) AS jum_cuti
FROM permohonan_cuti pc
RIGHT JOIN kat_cuti kc on kc.katCuti_id = pc.katCuti_id
GROUP BY kc.jenis_katCuti
Good day,
I am currently making a sales tracking report where the user pick a date range then the table should display the result.
for now I have this query that counts how many items are sold
select x_transaction_details.xitem,
SUM(x_transaction_details.qty) as totalNumberSold,
count(x_transaction_details.xitem) as occurence,
x_transaction_details.cost,
i_inventory.xitem,
x_transaction_details.date_at as transDate
from x_transaction_details
left join i_inventory on x_transaction_details.xitem = i_inventory.xid
where (x_transaction_details.date_at BETWEEN '2015-08-13 08:34:12' AND '2015-09-14 08:34:12')
GROUP BY x_transaction_details.xitem
ORDER BY occurence DESC
this query displays
|itemName| totalNumberSold | occurence | date
|item 1 | 23 pcs | 2 |
|item 2 | 18 pcs | 6 |
|item 3 | 203 pcs | 18 |
etc..
Now I want to know the breakdown of sales per day so I tried
select x_transaction_details.xitem,
SUM(x_transaction_details.qty) as sold,
count(x_transaction_details.xitem) as occurence,
x_transaction_details.cost,
i_inventory.xitem,
x_transaction_details.date_at as transDate
SUM(CASE WHEN date_at = DAYOFWEEK(1) THEN
count(x_transaction_details.xitem) END) as Sunday
from x_transaction_details
left join i_inventory on x_transaction_details.xitem = i_inventory.xid
where (x_transaction_details.date_at BETWEEN '2015-08-13 08:34:12' AND '2015-09-14 08:34:12')
GROUP BY x_transaction_details.xitem
ORDER BY occurence DESC
But its generating an error instead. I want to create a more detailed table
|itemName|Mon|Tue|Wed|Thur|Fri|Sat|Sun| totalNumberSold | occurence | date
|item 1 | 10| 0| 0 | 13 | 0 |0 |0 | 23 pcs | 2 |
|item 2 | 1 | 3| 12| 0 | 16|0 |0 | 32 pcs | 6 |
|item 3 | 0 | 6| 1 | 13 | 8 |7 |1 | 36 pcs | 12 |
etc..
Thanks for the tips, I can make this table using php (hard-way) too but I guess its doable using sql query as well. Have a good day ahead.
You are missing a comma and nesting aggregation functions. I think you want:
select i.xid, SUM(td.qty) as sold, count(td.xitem) as occurrence,
avg(td.cost) as avg_cost, i.xitem,
SUM(case when DAYOFWEEK(td.date_at) = 1 then td.qty else 0 end) as Sunday
from i_inventory i join
x_transaction_details td
on td.xitem = i.xid
where td.date_at BETWEEN '2015-08-13 08:34:12' AND '2015-09-14 08:34:12'
GROUP BY i.xid
ORDER BY occurrence DESC;
Notes:
"occurrence" has two r's.
Table aliases make the query easier to write and to read.
You can't nest aggregation functions.
You shouldn't include date_at in the select list because the value is not unique for each row.
The left join is either backwards (inventory should go first) or should be an inner join. In a properly formed database, you should not have items in the transaction table that are not in the inventory table (I don't think).
The actual code (might be helpful for anyone)
select x_transaction_details.xitem, count(x_transaction_details.xitem) as occurrence, i_inventory.xitem,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 1 then x_transaction_details.qty else 0 end) as Sun,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 2 then x_transaction_details.qty else 0 end) as Mom,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 3 then x_transaction_details.qty else 0 end) as tue,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 4 then x_transaction_details.qty else 0 end) as wed,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 5 then x_transaction_details.qty else 0 end) as thur,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 6 then x_transaction_details.qty else 0 end) as fri,
SUM(case when DAYOFWEEK(x_transaction_details.date_at) = 7 then x_transaction_details.qty else 0 end) as sat,
SUM(x_transaction_details.qty) as totalNumberSold
from x_transaction_details
left join i_inventory on x_transaction_details.xitem = i_inventory.xid
where (x_transaction_details.date_at BETWEEN '2015-08-13 08:34:12' AND '2015-09-14 08:34:12')
GROUP BY x_transaction_details.xitem
ORDER BY occurrence DESC
I did not used table aliases though :)
I have a simple table which holds the date and the total sales made for a certain group:
date | totalsales
=======================
2014-05-01 | 3000
2014-05-02 | 3100
2014-05-03 | 3500
2014-05-04 | 3650
I like to calculate some things like:
sales per day
average sales
growth in %
Result should look like (calculate by hand so maybe wrong :) )
date | sales | average | growth
=======================================
2014-05-01 | 0 | 0 | 0
2014-05-02 | 100 | 50 | 100
2014-05-03 | 400 | 166.66 | 400
2014-05-04 | 150 | 162.5 | 37.5
Is this even possible in a sql statement or should I calculate with PHP or another server software?
Assuming each date gets its own unique row, you could do it by joining back to your original table like so:
SELECT t1.Date, CASE WHEN t2.Date IS NULL THEN 0 ELSE (t1.totalsales - t2.totalsales)
END AS sales
FROM table t1
LEFT JOIN table t2 ON t2.Date = DATE_ADD(t1.Date, INTERVAL -1 DAY)
ORDER BY 1
This will give you at least your first column, and you should be able to figure out the math for the rest from there. It's important to use a left join with the CASE statement here because otherwise you won't get the lowest date in your table (your first row)
If each date does not get its own unique row, this is method is still viable, you just need to create your datasets in a subquery using GROUP BY and SUM on the date column.
Here is the full query with no subselect at each row : (Thanks to #nmarsh for writting the hardest part)
See SQL fiddle : http://sqlfiddle.com/#!2/be4654/34/0
SELECT
t1.Date,
CASE
WHEN t2.date IS NULL THEN 0 ELSE (t1.totalSales - t2.totalSales)
END AS sales,
CASE
WHEN t2.date IS NULL THEN 0 / (#curRow := #curRow + 1) ELSE ((#curSum := #curSum + (t1.totalSales - t2.totalSales)) / (#curRow := #curRow + 1))
END AS average,
CASE
WHEN t3.date IS NULL AND t2.date IS NULL THEN 0
WHEN t3.date IS NULL THEN (t1.totalSales - t2.totalSales)
WHEN t2.date IS NULL THEN 0 ELSE ((t1.totalSales - t2.totalSales) * 100) / (t2.totalSales - t3.totalSales)
END AS growth
FROM test t1
LEFT JOIN test t2 ON t2.date = DATE_ADD(t1.Date, INTERVAL -1 DAY)
LEFT JOIN test t3 ON t3.date = DATE_ADD(t2.Date, INTERVAL -1 DAY)
JOIN (SELECT #curRow := 0) r
JOIN (SELECT #curSum := 0) ct
ORDER BY 1;
Original table :
date | totalsales
=======================
2014-05-01 | 3000
2014-05-02 | 3100
2014-05-03 | 3500
2014-05-04 | 3650
OUTPUT
date | sales | average | growth
=======================================
2014-05-01 | 0 | 0 | 0
2014-05-02 | 100 | 50 | 100
2014-05-03 | 400 | 166.66 | 400
2014-05-04 | 150 | 162.5 | 37.5
You can use recursive statement. In each iteration, calculate the requested data for one day, and drop the first (oldest) day.
You can also do it with PHP, which seems better because you don't want to put too much load on the MySQL tables in case it does not saves you time/calculations.
Not too sure about the numbers, but if you are more specific about the results I can double check.
You can use ROW_NUMBER() to create 2 data sets and join them on ROW_NUMBER() and ROW_NUMBER()-1 to get an offset to current and previous values to calculate the growth. Sample :
DECLARE #Data TABLE (SalesDate DATETIME, totalSales INT)
INSERT INTO #Data (SalesDate , totalSales) VALUES ('2014-05-01' , 3000)
INSERT INTO #Data (SalesDate , totalSales) VALUES ('2014-05-02' , 3100)
INSERT INTO #Data (SalesDate , totalSales) VALUES ('2014-05-03' , 3500)
INSERT INTO #Data (SalesDate , totalSales) VALUES ('2014-05-04' , 3650)
SELECT
CurrentDt.SalesDate
,ISNULL(CurrentDt.totalSales - PreviousDt.totalSales ,0) AS Sales
,FirstDate.FirstDate
, NULLIF(CAST((CurrentDt.SalesDate - FirstDate.FirstDate) AS INT)+1,0) AS SellingDays
,(ISNULL(CurrentDt.totalSales - PreviousDt.totalSales ,0))
/ NULLIF(CAST((CurrentDt.SalesDate - FirstDate.FirstDate) AS INT)+1,0) AS AverageSales
FROM
(SELECT Min(SalesDate) AS FirstDate FROM #Data) AS FirstDate,
/*Base Sales Data*/
(
SELECT
ROW_NUMBER() OVER(ORDER BY SalesDate) AS RowNum
,SalesDate
,totalSales
FROM
#Data
) AS CurrentDt
/*Previous Value for Growth*/
LEFT JOIN
(
SELECT
ROW_NUMBER() OVER(ORDER BY SalesDate) AS RowNum
,SalesDate
,totalSales
FROM
#Data
) AS PreviousDt
ON CurrentDt.RowNum -1 = PreviousDt.RowNum
I have used MSSQL, but MySQL supports ROW_NUMBER OVER.
I hope this query help you
SELECT
sample.id,
sample.date AS oggi,
sample.value AS sales,
((SELECT SUM(sample.value) FROM sample WHERE sample.date <= oggi ) / (SELECT COUNT(sample.value) FROM sample WHERE sample.date <= oggi ) ) AS avarege,
sample.value / IF((SELECT sample.value FROM sample WHERE sample.date = (oggi - INTERVAL 1 DAY )) = 0,sample.value,(SELECT sample.value FROM sample WHERE sample.date = (oggi - INTERVAL 1 DAY ))) *100 AS 'growt-percent'
-- (SELECT SUM(sample.value) FROM sample WHERE sample.date <= oggi ) AS somma,
-- (SELECT count(sample.value) FROM sample WHERE sample.date <= oggi ) AS conta,
-- (SELECT sample.value FROM sample WHERE sample.date = (oggi - INTERVAL 1 DAY )) as valoreieri,
FROM sample
WHERE sample.date BETWEEN '2014-05-01 00:00:00' AND '2014-05-31 00:00:00'
table data is
id date value
1 2014-05-01 00:00:00 0
2 2014-05-02 00:00:00 100
3 2014-05-03 00:00:00 400
4 2014-05-04 00:00:00 150
5 2014-05-05 00:00:00 200
result is
id oggi sales avarege growt-percent
1 2014-05-01 00:00:00 0 0.0000 (NULL)
2 2014-05-02 00:00:00 100 50.0000 100.0000
3 2014-05-03 00:00:00 400 166.6667 400.0000
4 2014-05-04 00:00:00 150 162.5000 37.5000
5 2014-05-05 00:00:00 200 170.0000 133.3333
note that i use datetime field not only date
if you have question about query ask
sorry for my bad english
edit
the last 3 rows are commented because i used it only for test
I am using the following query,
SELECT
b.sales_id,b.category_id,a.bus_title,b.sales_title,b.sale_starts,b.sale_ends
FROM tbl_sales b INNER JOIN tbl_business a
ON a.bus_id=b.bus_id
WHERE b.active=1
AND CURDATE( ) < DATE_ADD(b.sale_ends, INTERVAL 14 DAY )
AND b.category_id='16' OR b.category_id
IN (SELECT cat_id FROM tb_category WHERE parent_id=16)
ORDER BY (b.sale_ends=CURDATE()) DESC,(b.sale_ends>CURDATE()) DESC,b.sale_ends ASC
and getting the result as shows below,
sales_id | category_id |bus_title | sales_title|sale_starts|sale_ends
----------|-------------|------------|------------|-----------|----------
36 | 17 | my bus | my sale |2012-04-03 |2012-05-03
35 | 19 | my bus 1 | my sale 1 |2012-04-03 |2012-05-03
42 | 16 | my bus 12 | my sale 12 |2012-04-05 |2012-05-05
10 | 17 | my bus 123| my sale 123|2011-12-15 |2011-12-18
I need to omit(not delete) sales after X amount of days past the End Date (here after 14 days). But the MySQL query i had written above returns the wrong results and displays the records with End Date 2011-12-18. How can i write the query in an effective way.
Need help. Thanks in advance.
Add this to your query :
AND sale_ends < DATE_SUB(NOW(), INTERVAL 14 day)