I have a table called 'ratings' which contains an index which is a unique ascending number, ProductId which is a product code, rate1 to rate5 are the number of times each product has recieved a 1 or 5 star rating by a customer aveRate is the average rating for each product and lastSubDate should be the timestamp of when the last review for each product was submitted but it's currently empty.
id ProductId rate1 rate2 rate3 rate4 rate5 aveRate lastSubDate
18 9996637 0 0 0 0 1 5 0000-00-00 00:00:00
26 9996628 1 0 0 0 0 1 0000-00-00 00:00:00
34 9996618 0 0 0 1 0 4 0000-00-00 00:00:00
36 9996614 5 0 0 0 0 1 0000-00-00 00:00:00
48 9996592 5 0 1 0 3 3 0000-00-00 00:00:00
66 9996566 0 0 0 1 3 5 0000-00-00 00:00:00
In another table I have called 'last_rate_date' I have ProductId and the date the product was last rated under lastSubDate. In this table the Product Id's can appear several times as per the example as each row represents a time a review was submitted by a customer.
ProductId lastSubDate
9996665 2009-05-22 19:45:05
9996665 2009-08-06 11:30:07
9996665 2010-11-10 08:30:17
9996665 2011-06-10 09:15:47
9996665 2011-06-12 05:15:39
My questions is how can I modify the first table 'ratings' using SQL to insert the last time a product was reviewed under the 'lastSubDate' using the second table 'last_rate_date' to lookup the productId and the date bearing in mind each ProductId may appear mutiple times in this seconds table with differen't dates.
This needs to be one as a one off table modification to the first table. Adter this is done I will amend the script which adds data to ratings table to always add a timestamp in the future when it's updated.
You can get most recent dates for each "ProductId" using the query below:
Select ProductId, MAX(lastSubDate)
From last_rate_date
Group By ProductId
This will produce each ProductId that appears in the second table, along with the latest date (using the MAX function). The Group By clause is needed to define what the aggregation is run against.
To further solve your problem, you can insert this Select statement into an Update statement like so:
UPDATE ratings a
INNER JOIN (
Select ProductId, MAX(lastSubDate) as finalLastSubDate
From last_rate_date
Group By ProductId ) b
ON a.ProductId = b.ProductId
SET a.lastSubDate = b.finalLastSubDate
You can use the timestamp with CURRENT_TIMESTAMP default
or you can add the date on the insert.
$date = date ("m-Y-d, H: i: s")
Related
I have an 'actions' table in the following format:
id
action
category_id
created_date
1
fff
3
12/11/2020
2
aaa
7
12/04/2021
3
bbb
3
04/01/2016
which is in a one-to-many relationship with 'categories' table (one category can have many actions, category_id is the foreign key above) in the following format:
id
name
1
Cat-1
2
Cat-2
I need an API that receives a date range (start and end values) as an input an should return the total number of actions for all categories that take place for every day within that range, like so:
day
Cat-1
Cat-2
10/1/2020
22
56
06/8/2011
56
78
Basically showing how many actions of a particular category took place per day in that date range.
My initial way of solving this was to fetch the data grouped by date and then manipulate that in the code. But I want to know if there's a way to do achieve this with SQL itself.
select created_date as day
,count(case when name = 'Cat-1'then 1 end) as 'Cat-1'
,count(case when name = 'Cat-2'then 1 end) as 'Cat-2'
from t join t2 using(id)
group by created_date
day
Cat-1
Cat-2
2020-12-11
1
0
2021-12-04
0
1
Fiddle
I have two tables
Table_1 : Routes_Day_plan
Date Status_Id
------------------------
2019-06-09 1
2019-06-10 2
2019-06-09 2
2019-06-11 3
2019-06-14 4
2019-06-14 6
2019-06-15 8
Table_2 : Codes
id code
-------
1 Leave
2 Half_leave
3 Holiday
4 Work
5 Full_Hours
Now my task is to count week wise from table 1 where code (from second table) = Leave,Half_leave,work and than also show the sum , and where date not found show 0 , i write this query it's return data but not empty dates can someone please help ,
My Query:
select COUNT(*) as available, DATE(date)
from Table_1
where status_id in (
select id from codes
where code in ('Leave','Half_leave','work'))
AND DATE(date) >= DATE('2019-06-09') AND DATE(date) <= DATE('2019-06-16')
group by date
UNION ALL
SELECT COUNT(date), 'SUM' date
FROM Table_1
where status_id in (
select id from codes
where code in ('Leave','Half_leave','work'))
AND DATE(date) >= DATE('2019-06-09') AND DATE(date) <= DATE('2019-06-16')
Result Something Like ,
available Dates
------------------------
5 2019-06-09
2 2019-06-10
3 2019-06-11
3 2019-06-12
2 2019-06-14
2 2019-06-15
17 SUM
I want like this
available Dates
------------------------
5 2019-06-09
2 2019-06-10
3 2019-06-11
3 2019-06-12
0 2019-06-13
2 2019-06-14
2 2019-06-15
17 SUM
Your best bet here would be to have a Date Dimension/Lookup table which contains pre-populated dates for the entire year. By joining your record table to this lookup, you essentially allocate your data to each date that actually exist (ex. 2019-06-13) and if your data is not found in the lookup, you will find a null in that field.
The Count function will count a null as a 0. Just make sure you group on the date field from your lookup table and not from your record table.
Make a table, a date dimension that contains all the dates value, from beginning to end. Like this:
Set EndDate = '2099-01-01';
Set RunDate = '1900-01-01';
WHILE RunDate <= EndDate DO
insert into dim_date
(`DATE`)
select
RunDate as DATE
;
Set RunDate = ADDDATE(RunDate,1);
END WHILE;
Create temporary table with dim_date left join Routes_Day_plan and set Status as 0 maybe for record that dont match. Use this temporary table then instead of Routes_Day_plan in your queries.
Let's say you have a user table that has at least the date the user signed up and an id.
Now let's say you have a separate table that tracks an action like a payment that can happen at any point in the user's lifetime. (Say like an in-app purchase.) In that table we track the userId, payment date, and an id for the payment.
So we have something that looks like this to get our schema set up:
CREATE TABLE users (
UserId INT,
AddedDate DATETIME
);
CREATE TABLE payments (
PaymentId INT,
UserId INT,
PaymentDate Datetime
);
Now you want a table that shows weekly cohorts. A table that looks something like this:
Week size w1 w2 w3 w4 w5 w6 w7
2017-08-28 1 0 0 0 1 0 0 0
2017-09-04 3 1 0 2 0 1 1 2
2017-09-11 2 0 0 1 0 0 0 1
2017-09-18 6 3 1 4 3 1 1 2
2017-09-25 2 1 1 1 0 1 2 0
2017-10-02 7 5 2 3 4 3 1 0
2017-10-09 7 4 5 1 2 5 0 0
2017-10-16 2 1 2 1 1 0 0 0
2017-10-23 7 5 4 4 3 0 0 0
2017-10-30 8 8 7 0 0 0 0 0
2017-11-06 5 5 2 0 0 0 0 0
So the first column has the week, the second has number of people that signed up that week. Say we look at week 2017-09-18. 6 people signed up that week. The 3 under the w1 column means that 3 people out of that 6 made a purchase the week they signed up. The 1 under w2 means 1 person out of that 6 made a purchase the second week they were signed up, and so on.
What query would I use to get a table that looks like that?
This query is modified from the one I wrote here: Cohort analysis in SQL
Here's the final query:
SELECT
STR_TO_DATE(CONCAT(tb.cohort, ' Monday'), '%X-%V %W') as date,
size,
w1,
w2,
w3,
w4,
w5,
w6,
w7
FROM (
SELECT u.cohort,
IFNULL(SUM(s.Offset = 0), 0) w1,
IFNULL(SUM(s.Offset = 1), 0) w2,
IFNULL(SUM(s.Offset = 2), 0) w3,
IFNULL(SUM(s.Offset = 3), 0) w4,
IFNULL(SUM(s.Offset = 4), 0) w5,
IFNULL(SUM(s.Offset = 5), 0) w6,
IFNULL(SUM(s.Offset = 6), 0) w7
FROM (
SELECT
UserId,
DATE_FORMAT(AddedDate, "%Y-%u") AS cohort
FROM users
) as u
LEFT JOIN (
SELECT DISTINCT
payments.UserId,
FLOOR(DATEDIFF(payments.PaymentDate, users.AddedDate)/7) AS Offset
FROM payments
LEFT JOIN users ON (users.UserId = payments.UserId)
) as s ON s.UserId = u.UserId
GROUP BY u.cohort
) as tb
LEFT JOIN (
SELECT DATE_FORMAT(AddedDate, "%Y-%u") dt, COUNT(*) size FROM users GROUP BY dt
) size ON tb.cohort = size.dt
So the core of this is we grab the users and the date they signed up and format the date by year-week number, since we are doing a weekly cohort.
SELECT
UserId,
DATE_FORMAT(AddedDate, "%Y-%u") AS cohort
FROM users
Since we want to group by the cohort we have to put this in a subquery in the FROM part of the query.
Then we want join the payment information on the users.
SELECT DISTINCT
payments.UserId,
FLOOR(DATEDIFF(payments.PaymentDate, users.AddedDate)/7) AS Offset
FROM payments
LEFT JOIN users ON (users.UserId = payments.UserId)
This will get unique weekly payment events per user by the numbers of weeks they have been a user. We use distinct because if a user made 2 purchase in one week, we don't want to count that as two users.
We don't just use the payments table, because some users may sign up and not have payments. So we select from the users table and join on the payments table.
You then group by the week - u.cohort. Then you aggregate on the week numbers to find out how many people made payments the weeks after they signed up.
The version of mysql I used had sql_mode set to only_full_group_by. So to get the cohort size I put the bulk of the query in subquery so I could join on the users to get the size of the cohort.
Further considerations:
Filter by weeks is simple. tb.cohort > start date and tb.cohort < end date where start and end date are formatted with "%Y-%u". To make the query more efficient you'll probably want to filter out payment events that don't fall within the date range as well so you're not joining on data you don't need.
You may want to consider using a calender table to cover cases where there are no user sign ups during the week.
Here's a fiddle with everything working: http://sqlfiddle.com/#!9/172dbe/1
To sort by months, you need to transfer the month to Offset
MONTH(payments.PaymentDate) AS Offset
Also to add a date selection with months
DATE_FORMAT(AddedDate, "%Y-%m") AS cohort_month
And add
ORDER BY tb.cohort_month ASC
I have data like this
--------------------------------
Date Serial State
2016-04-30 20:34:47 4 0
2016-04-30 20:34:48 5 0
2016-04-30 20:35:10 4 0
2016-04-30 20:35:08 5 1
I am trying to query the latest timestamp for each serial with it's associate state so it would be like this
Date Serial State
2016-04-30 20:35:10 4 0
2016-04-30 20:35:08 5 1
This seemed to grab me the latest date and group the serials but the state column doesnt change for some reason and stays at 0
Select MAX(date) as date, serial AS serial, state AS state
FROM testGraph
GROUP BY serial
You can do this in the where clause:
select tg.*
from testgraph tg
where tg.date = (select max(tg2.date) from testgraph tg2 where tg2.serial = tg.serial);
I am unable to write a query which needs the following :
I have 4 tables of Game products:
Table 1 (Soccer)
Product_Name
SoccerBall
SoccerShoes
SoccerShins
Table2 (Cricket)
Product_Name
CricketBall
CricketStumps
CricketBat
Table3(Rugby)
Product_Name
RugbyBall
Table4(Pingpong)
Product_Name
Pingpongball
I also have a table generating a revenue about all the products which is as follows:
Table5
Userid OrderSno Product_Name OrderTime Revenue
123 66243 CricketBall 12Jan2012 35
123 66553 CricketBat 15June2013 60
123 36476 SoccerBall 15Dec2013 15
The result table should be something like this :
Ordertime(Sorted) Cricket(3months) Cricket(6months) Cricket(Lifetime)
12Jan2012 0 0 0
15June2013 0 0 35
15 Dec2013 0 60 95 (60+35)
Soccer(3 months) Soccer(6months) Soccer(Lifetime)
0 0 0
0 0 0
0 0 0
The above table gives the revenue generated for every product purchased before that particular product was purchased. This is based on the orderdate/time sorted.
For example : The first order placed by user 123 was on 12Jan2012. So that user had not purchased anything before that since it was his first order. Hence the first row of the result table should be 0.
Coming to the 2nd row, the 2nd purchase that he made was on 15June2013. SO the result table should contain all the revenue for the respective product type before 2nd order was made. Hence in this case 35 would be there under Cricket field (Since the Product_name belongs to Cricket table) and it would fall into the Lifetime field. This is because the order purchase date is 15June2013. So 3 months before this nothing was purchased. Similarly 6 months before this date nothing was purchased. But before 1 year or more than that Cricket Ball was purchased which generated a revenue of 35. Hence the value of 35 should fall into Lifetime field of Cricket based on the Product_Name of Cricket Ball.
The same thing should happen for all the products. I know the query is complex and i am not sure whether this is feasible or not. Since i am new to any help regarding this would be appreciated.
Well I am not sure if this exactly what you want but I think its work taking a look at. If you want all the sport in one table a Union would do it.
Select Product_Name
,YEAR(ordertime)
,SUM(case when MONTH(ordertime) in (1,2,3) then revenue else 0 end) 'Q1'
,SUM(case when MONTH(ordertime) between 1 and 6 then revenue else 0 end) 'Q2'
,SUM(case when MONTH(ordertime) between 1 and 9 then revenue else 0 end) 'Q3'
,SUM(revenue) 'Q4'
from PingPong
group by Product_Name, YEAR(ordertime)