I have a task to find the total amount of money paid during weekdays vs weekends.
So first I have to separate the weekdays from the weekends then find the toal amount of money paid on each, per month.
The tables I am using have the following structure
Game_users table
game_id | user_id |bill_id| created_at | deleted_at
1234 | 244 | 456 |2016-10-08 14:47:11 | null
Bills Table
bill_id | user_id |amount | created_at | deleted_at
1234 | 244 | 100 |2016-10-08 14:47:11 | null
So, I have to sum(amount) and then separate them into weekdays and weekends per month
So group by month(created_at)
I need a query to achieve this expected result
Month | count(weekdays) | Total Amount Paid
1 | 34 | 500.0
Related
The problem looks like this:
Write an SQL query that selects the product id, year, quantity, and price for the first year of every product sold.
Return the resulting table in any order.
A simple example of the question:
Input:
Sales table:
+---------+------------+------+----------+-------+
| sale_id | product_id | year | quantity | price |
+---------+------------+------+----------+-------+
| 1 | 100 | 2008 | 10 | 5000 |
| 2 | 100 | 2009 | 12 | 5000 |
| 7 | 200 | 2011 | 15 | 9000 |
+---------+------------+------+----------+-------+
Product table:
+------------+--------------+
| product_id | product_name |
+------------+--------------+
| 100 | Nokia |
| 200 | Apple |
| 300 | Samsung |
+------------+--------------+
Output:
+------------+------------+----------+-------+
| product_id | first_year | quantity | price |
+------------+------------+----------+-------+
| 100 | 2008 | 10 | 5000 |
| 200 | 2011 | 15 | 9000 |
+------------+------------+----------+-------+
My code:
SELECT product_id, year AS first_year, quantity, price
FROM Sales
WHERE year IN (
SELECT MIN(year) as year
FROM Sales
GROUP BY product_id) ;
My code worked with the above simple example but failed on a longer test case.
The expected query:
SELECT product_id, year AS first_year, quantity, price
FROM Sales
WHERE (product_id, year) IN (
SELECT product_id, MIN(year) as year
FROM Sales
GROUP BY product_id) ;
So I don't understand why I have to put productid when filtering with where. Doesn't SQL automatically choose the corresponding product_id with the first year? Any hints would be greatly appreciated!
Here's a step-by-step of why the first query doesn't work. Note that I've omitted some fields that are unused for the sake of brevity.
Imagine your sales data contained the following data:
product_id
year
100
2008
100
2011
100
2011
100
2011
100
2011
200
2011
Based on this data, the inner subquery of your first query:
SELECT MIN(year) as year
FROM Sales
GROUP BY product_id
will produce a result as follows:
MIN(year)
2008
2011
And so then your query is effectively doing the following:
SELECT product_id, year AS first_year
FROM Sales
WHERE year IN (2008, 2011)
So this query is going to find all the sales that occurred in 2008 and all the sales that occurred in 2011. It is not going to filter by product_id as that is not specified in the WHERE statement. So it'll yield results as follows which is not what you want:
product_id
first_year
100
2008
100
2011
100
2011
100
2011
100
2011
200
2011
This is why you need to specify the product_id in your IN statement.
On a general note, when debugging SQL, evaluate the inner-most queries first and then work outward as I have done in this answer.
I have a table that looks like this:
+--------+---------------------+-------+--------+-----------+
| PartNo | Date | Inv | Retail | Wholesale |
+--------+---------------------+-------+--------+-----------+
| 1 | 2018-05-12 00:00:00 | 15 | $100 | $90 |
| 2 | 2018-05-12 00:00:00 | 20 | $200 | $150 |
| 3 | 2018-05-12 00:00:00 | 25 | $300 | $200 |
| 1 | 2018-05-13 00:00:00 | 10 | $95 | $90 |
| 2 | 2018-05-14 00:00:00 | 15 | $200 | $150 |
| 3 | 2018-05-14 00:00:00 | 20 | $300 | $200 |
+--------+---------------------+-------+--------+-----------+
And I want it to look like this with a Mysql query:
+--------+------+--------+
| PartNo | Sold | Profit |
+--------+------+--------+
| 1 | 5 | $25 |
| 2 | 5 | $250 |
| 3 | 5 | $500 |
+--------+------+--------+
I need to group by PartNo while calculating the difference between totals and profits over a date range.
The unit profit has to be calculated by subtracting the wholesale from retail on the last day (or record) of the date range.
I feel like this should be easy but the differences over the date ranges are confusing me and handling records within the date range that don't start or end exactly on the date range input are losing me.
Any help would be super appreciated.
Thank you.
You can look up the situation at the start and at the end of the period If no start situation is found, assume no stock. If no end situation is found, that means no sales during the period.
For example for the period starting 2018-05-13 and ending 2018-05-14:
select parts.PartNo
, coalesce(FirstSale.Total, 0) - coalesce(LastSale.Total, FirstSale.Total, 0) as Sold
, (coalesce(FirstSale.Total, 0) - coalesce(LastSale.Total, FirstSale.Total, 0)) *
coalesce(LastSale.Retail - LastSale.Wholesale, 0) as Profit
from (
select PartNo
, max(case when Date < '2018-05-13' then Date end) as FirstEntry
, max(case when Date <= '2018-05-14' then Date end) as LastEntry
from Sales
group by
PartNo
) parts
left join
Sales FirstSale
on FirstSale.PartNo = parts.PartNo
and FirstSale.Date = parts.FirstEntry
left join
Sales LastSale
on LastSale.PartNo = parts.PartNo
and LastSale.Date = parts.LastEntry
Example at SQL Fiddle.
SELECT c.partno as partno,MAX(c.inv)-MIN(c.inv) as sold,SUM(CASE WHEN c.date = c.last_date THEN profit else 0 END)*(MAX(c.inv)-MIN(c.inv)) as profit
FROM (SELECT partno,date,inv,retail-wholesale as profit,MAX(date) OVER (partition by partno) AS last_date FROM test1)c
GROUP BY c.partno
ORDER BY c.partno;
Using the window function, first append a new column to track the max date for each partno. So the inner query inside FROM will produce rows like these with one column added to the the original dataset,
| 1 | 2018-05-12 00:00:00 | 15 | $100 | $90 | **2018-05-13 00:00:00** |
The highlighted field is the one added to the dataset which is the last date in the date range for that part number!
Now from this result, we can pull out profit by checking for the row in which date column is equal to the new column we appended, which is essentially calculating the profit for the last date by subtracting wholesale from retail and multiplying with items sold.
PS : The logic for items sold is grouping by partno and subtracting MIN(Inv) from MAX(Inv)
Link to SQL Fiddle
Storage table
| id| product_id | date_add | date_remove
------------------------------------------------------------------
| 1 | 10 |2018-04-02 08:28:43 | 2018-04-03 07:21:08
| 2 | 10 |2018-04-05 08:28:43 | 2018-04-06 08:28:50
| 3 | 10 |2018-04-01 08:28:43 | 2018-04-05 08:28:50
| 4 | 12 |2018-04-01 08:28:43 | 2018-04-03 07:21:08
| 5 | 12 |2018-04-04 08:28:43 | 2018-04-04 10:28:43
| 6 | 13 |2018-03-01 08:28:43 | 2018-03-01 10:28:43
how to find ?
how many days product was in the storage in period 2018-04-01 to 2018-04-05?
find result
| product_id | days
| 10 | 5
| 12 | 3
try
SELECT product_id, SUM(DATEDIFF(date_remove, date_add)) as days
FROM storage
where date_remove BETWEEN '2018-04-01 00:00:00'
AND '2018-04-05 23:59:59'
AND date_add BETWEEN '2018-04-01 00:00:00'
AND '2018-04-05 23:59:59'
GROUP BY product_id
but result wrong because 'SUM' sums all days
get result
| product_id | days
| 10 | 7
correct result
| product_id | days
| 10 | 5
upd
http://rextester.com/QFS96125
result 9,646805555556 but probably maximum 5 days and product_id 13 correct 0,436608796296 but result 0,87
First you want to look at all date ranges that are within or overlap with the range 2018-04-01 to 2018-04-05.
where date_add < date '2018-04-06' and date_remove >= date '2018-04-01'
Then, with the ranges found, you want to consider only their days in the range 2018-04-01 to 2018-04-05.
greatest(date_add, date '2018-04-01')
least(date_remove, date '2018-04-06')
Then you want to count days. Here you need a rule. Do you want to look at single ranges and only take their full days which you add up then? Or do you want to consider day fractions, add all up and see how many full days result? For the latter you could get durations in seconds and add these up:
select
product_id,
sum(timestampdiff(second, greatest(date_add, date '2018-04-01'),
least(date_remove, date '2018-04-06'))
) / 60 / 60 / 24 as days
from storage
where date_add < date '2018-04-06' and date_remove >= date '2018-04-01'
group by product_id
order by product_id;
This gets you
product_id | days
-----------+---------------
10 | 5,599872685185
12 | 2,036400462963
Feel free to use FLOOR, CEIL or ROUND on the resulting days.
Rextester demo: http://rextester.com/XTVU47656
To obtain such result, try
SELECT SUM(DATEDIFF(date_remove, date_add)) as days
FROM table
GROUP BY product_id
Keep in mind that this will sum all the days for the same product_id. To get the result for each id, use:
SELECT id, product_id, DATEDIFF(date_remove, date_add) as days
FROM table
have this portion of a large table T;
+---------------+------------+
| session_id | visit_time |
+---------------+------------+
| 4f89cebc109f9 | 1334431476 |
| 4f89cf283d21c | 1334431528 |
| 4f89cf283d21c | 1334431534 |
| 4f89cf3b350a6 | 1334431547 |
| 4f89cf42ab640 | 1334431554 |
+---------------+------------+
I want to find number of session_id weekday-wise. Session_id is not primary key. So I tried:
select count(distinct(session_id)) from T group by weekday(from_unixtime(time))
But it won't work because if same session_id has visit_time on two different sundays, then it counts them as 1, although it rightly counts 1 when same session_id has visit_time on same sunday.
The expected thing is : I want to know how many session-ids have visit day as sun, mon etc. If a session_id has visited on two different sundays, then they are counted twice, but if on same sunday, then only 1 count.
So how can I do it in Mysql ?
use WEEK instead of WEEKDAY
SELECT WEEK(FROM_UNIXTIME(time)) WeekNo,
DATE_FORMAT(FROM_UNIXTIME(time),'%a') WeekName,
COUNT(DISTINCT(session_id))
FROM T
GROUP BY WEEK(FROM_UNIXTIME(time)),
DATE_FORMAT(FROM_UNIXTIME(time),'%a')
WEEK
I have two tables, invoices and deposits.
They look a bit like this:
INVOICES
id | paymentType | grossTotal | dateTime
1 | Cash | 1000 | UNIX TIME
2 | Card | 1350 | UNIX TIME
3 | Card | 1250 | UNIX TIME
4 | Card | 750 | UNIX TIME
DEPOSITS
id | paymentType | invNo | dateTime | amount
1 | Cash | 1 | UNIX TIME | 150
2 | Card | 2 | UNIX TIME | 350
The deposits are always past dates, and the invoice dates will be, for example today, so I want to determine the balance paid today on an invoice, ie, invoices.grossTotal - deposits.amount, and list by payment Type.
So, in the table example above, there is £850 would have been paid on Invoice 1 and £1000 on invoice 2, this is simple to achieve with one or two rows of each, but when grouping payment types and deposit invoices I am stuck...
SELECT
invoices.id,
sum(grossTotal)-IFNULL(depositsCheck.previouslyPaid,0) as todayTotal,
depositsCheck.previouslyPaid, sum(grossTotal) as grossTotal
FROM `invoices`
LEFT JOIN (SELECT SUM(amount) as previouslyPaid, invNo
FROM deposits
GROUP BY invNo) depositsCheck ON depositsCheck.invNo=invoices.id
GROUP BY invoices.paymentType ORDER BY id DESC
The SQL Query above, will work for the item paid for with CASH, but not for the Card payments, because, grouping invoices.paymentType means that the id column from the invoices table is no longer correct, so the JOIN fails if this row has an id to which no deposit relates.
How can I run a query as above, but ensuring that I can join the deposits table on any instance of an invoice, grouped by payment type that matches the grouped column id records?
I am using mySql, so please post joins that MySql can do! :D
The problem is that when you use GROUP BY, you can only SELECT aggregates and the columns you have grouped on.
invoices.id is a column you tried to select, but did not group. I think you probably want to add this column to the GROUP BY clause.
SELECT
invoices.id,
sum(grossTotal)-IFNULL(depositsCheck.previouslyPaid,0) as todayTotal,
depositsCheck.previouslyPaid, sum(grossTotal) as grossTotal
FROM `invoices`
LEFT JOIN (SELECT SUM(amount) as previouslyPaid, invNo
FROM deposits
GROUP BY invNo) depositsCheck ON depositsCheck.invNo=invoices.id
GROUP BY invoices.paymentType, invoices.id ORDER BY id DESC
For the example tables you gave, it will probably give:
id | paymentType | grossTotal | dateTime | previouslyPaid
1 | Cash | 1000 | UNIX TIME | 150
2 | Card | 1350 | UNIX TIME | 350
3 | Card | 1250 | UNIX TIME | 0
4 | Card | 750 | UNIX TIME | 0
But in general, you will have something like:
id | paymentType | grossTotal | dateTime | previouslyPaid
1 | Cash | 1000 | UNIX TIME | 150
1 | Card | 1000 | UNIX TIME | 300
2 | Cash | 1350 | UNIX TIME | 350
2 | Card | 1350 | UNIX TIME | 350
Where you can see above, for invoice 1, 150 was paid in cash, and 300 was paid by card.