I have the following sample data:
id, user_id, action, date, item_id
(5, 1, 'created', '2016-09-08, 1),
(6, 1, 'sold', '2016-09-14, 1),
(7, 2, 'created', '2016-09-08, 2),
(8, 2, 'sold', '2016-09-30, 2),
(9, 3, 'created', '2016-10-08, 3)
I'm trying to create a Query that returns the percentage of items sold within 1 week. The value of the column: "action" represents if the item has been put up for sale, or sold. How could this look?. Should I do this by using a subquery or?
Expected result should be a single percentage (the number of items sold within 1 week, of the total number of items created).
Assuming that the data is indeed this simple, this can easily be done by joining the same table to itself. The first reference to the sample data can be aliased as created and will filter to items with an action of created. Likewise, the sold table reference will restrict itself to items with an action of sold.
Once that's done, we'll get a row of data that has an item's creation and sold dates. Anything that doesn't have a sold action is simply discarded by an inner join. The built in function datediff(date1, date2) will give us the number of days between our two dates. If this is less than or equal to 7, you know that it was sold within a week.
select
created.id
, created.user_id
, created.item_id
, datediff(created.date, sold.date) as days_to_sell
from
sample_data created
join sample_data sold
on created.item_id = sold.item_id
where
created.action = 'created'
and sold.action = 'sold'
and datediff(created.date, sold.date) <= 7
Related
I have original data like this.
Original Data
I need to create two report with it, this is the first report :
First Report
The running value can be achieved with this expression
RunningValue(Fields!City.Value+Fields!Month.Value,CountDistinct,"Region")
The second report i need is this:
Second Report
What can i do to add logic to the running value so it can avoid numbering row with Sum(Amount) zero ?
I'm not sure you can do this using RunningValue, other people may know of a way.
What I did was move the logic to the query.
I reproduced some data to match your final report numbers (your sample data does not match the sample report output).
Here's the sample data I used.
DECLARE #t TABLE(Region varchar(10), City varchar(10), MonthID int, Amount int)
INSERT INTO #t VALUES
('Asia', 'Tokyo', 4, 1000),
('Asia', 'Tokyo', 4, 500),
('Asia', 'Tokyo', 5, 2000),
('Asia', 'Tokyo', 5, -2000),
('Asia', 'Tokyo', 6, 1000),
('Asia', 'Tokyo', 6, -500),
('Asia', 'Bangkok', 4, 500),
('Asia', 'Bangkok', 4, 500),
('Asia', 'Bangkok', 5, 3000),
('Asia', 'Bangkok', 5, -500),
('Asia', 'Bangkok', 6, -750),
('Asia', 'Bangkok', 6, 750)
SELECT
*
, ROW_NUMBER() OVER(PARTITION BY Region, City ORDER BY MonthID) as RowN1
, ROW_NUMBER() OVER(PARTITION BY (CASE Amount WHEN 0 THEN 0 ELSE 1 END), Region, City ORDER BY MonthID) as RowN2
FROM
(
SELECT
Region, City, MonthID
, SUM(Amount) AS Amount
FROM #t
GROUP BY Region, City, MonthID
) x
ORDER BY Region, City DESC, MonthID
I used the ROW_NUMBER function to assign a row numbers for both reports.
The first one "RowN1" is a simple row number within city
The second one "RowN2" does the same thing but it partitions any zero values so they are not in the same partition as the other data.
This gives us the following dataset
Now you can use a simple table to display the result in your first report using RowN1
In your second report use RowN2 with the expression
=IIF(Fields!Amount.Value=0, Nothing, Fields!RowN2.Value)
This simply forces a blank to be displayed if the amount is zero.
I did this and got the following results.
Note: I used a month number in the data just to make sorting easier, in the report I used =MonthName(Fields!MonthID.Value) to show the actual name.
I am developing a employee login system in which user check in and checkout timings are recorder. I have the following mySql table schema from which I would like to query the total working hours of an employee of a particular month.
AttendanceId UserId Operation CreatedDate
24 4 1 2016-03-20 23:18:59
25 4 2 2016-03-20 23:19:50
26 4 1 2016-03-20 23:20:28
27 4 2 2016-03-20 23:20:31
Operation 1 is for check in and operation 2 is for checkout. Can any one help me to build this query?
A pleasingly complicated question, thanks. My query deals with:
Attendances that aren't precisely measured in hours. The number of seconds is totalled and divided by 3600 at the end of the calculation.
Attendances that span the month boundary at either end (thanks strawberry)
Attendances in the current month that have started (there is an entry with operation "1") but not yet finished (there is no corresponding operation "2").
I used the following data for testing:
INSERT INTO Attendance(UserId, Operation, CreatedDate) VALUES
(4, 1, '2016-01-01 15:00:00'),
(4, 2, '2016-01-01 19:00:00'),
(4, 1, '2016-01-31 23:00:00'),
(4, 2, '2016-02-01 01:00:00'),
(4, 1, '2016-02-20 23:18:59'),
(4, 2, '2016-02-20 23:19:50'),
(4, 1, '2016-02-20 23:20:28'),
(4, 2, '2016-02-20 23:20:31'),
(4, 1, '2016-02-29 23:00:00'),
(4, 2, '2016-03-01 01:00:00'),
(4, 1, '2016-03-02 15:00:00'),
(4, 2, '2016-03-02 18:00:00'),
(4, 1, '2016-03-22 10:00:00');
The query selects all users' hours for a specific month. Selecting results for more than one month in one query is more complicated because of the possibility that attendances span month boundaries and if required it might be simplest to iterate over the months and run the query repeatedly, adjusting the four dates in the SQL appropriately.
The innermost query selects all arrival times and the corresponding departure time for all users. The outer query then restricts them to the current month, calculates the difference between the two times, and sums them by user.
SELECT UserId, SUM(TIMESTAMPDIFF(
SECOND,
GREATEST(TimeIn, '2016-02-01'),
LEAST(COALESCE(TimeOut, NOW()), '2016-03-01'))) / 3600 HoursInMonth
FROM (SELECT TimeIn.UserId, TimeIn.CreatedDate TimeIn, MIN(TimeOut.CreatedDate) TimeOut
FROM Attendance TimeIn
LEFT JOIN Attendance TimeOut ON TimeOut.UserId = TimeIn.UserId
AND TimeOut.Operation = 2
AND TimeOut.CreatedDate > TimeIn.CreatedDate
WHERE TimeIn.operation = 1
GROUP BY TimeIn.AttendanceId
ORDER BY TimeIn.CreatedDate) TimeInOut
WHERE DATE_FORMAT(TimeIn, '%Y-%m') = '2016-02'
OR DATE_FORMAT(TimeOut, '%Y-%m') = '2016-02'
OR (DATE_FORMAT(TimeIn, '%Y-%m') < '2016-02' AND TimeOut IS NULL)
GROUP BY UserId;
I've got a table with three key attributes, like below:
I want to query it to figure out the number of items sold based on item type and date sold. So the output might look something like this:
My query looks like this:
SELECT SaleItem AS 'Item Sold', SaleDay AS 'Item Sell Day', COUNT(DISTINCT SaleNumber) AS 'Number'
FROM Sales
GROUP BY SaleDay, SaleItem
This is returning a table with the SaleItems organized properly, but the count is the total number sold, excluding the SaleDay attribute.
It's returning the total number of sales per item instead of only the sales on that day per item. So, if 7 pens were sold Monday and 6 on Tuesday it should return two tuples (7, Monday, Pen) and (6, Tuesday, pen). Currently, its returning (13, Monday, pen) and (13, Tuesday, pen).
How can I fix this query?
You complecated your question, by next simply show the input & output so you will get answer faster, any way, here is your solution: you don't need the distinct
SELECT SaleItem AS 'Item Sold',
SaleDay AS 'Item Sell Day',
COUNT(SaleItem) AS 'Number'
FROM Sales
GROUP BY SaleDay, SaleItem
Each staff already has a table of avail time slots in AvailSlots like this:
Staff_ID Avail_Slots_Datetime
1 2015-1-1 09:00:00
1 2015-1-1 10:00:00
1 2015-1-1 11:00:00
2 2015-1-1 09:00:00
2 2015-1-1 10:00:00
2 2015-1-1 11:00:00
3 2015-1-1 09:00:00
3 2015-1-1 12:00:00
3 2015-1-1 15:00:00
I need to find out which staff has, for example, 2 (or 3, 4, etc) CONSECUTIVE avail time slots at each time slot. As a novice, the INNER JOIN codes below is all I know to write if the query is for 2 consecutive time slots.
SELECT a.start_time, a.person
FROM a_free a, a_free b
WHERE (b.start_time = addtime( a.start_time, '01:00:00' )) and (a.person = b.person)
But, obviously, doing it that way, I would have to add more INNER JOIN codes - for each case - depending on whether the query is for 3, or 4, or 5 , etc consecutive available time slots at a given date/hour. Therefore, I want to learn a more efficient and flexible way to do the same. Specifically, the query code I need (in natural language) would be this:
For each time slot in AvailSlots, list one staff that has X (where X can
be any number I specify per query, from 1 to 24) consecutive datetime
slot starting from that datetime. In case more than one staff can meet
that criteria, the tie break is their "rank" which is kept in a
separate table below:
Ranking Table (lower number = higher rank)
Staff_ID Rank
1 3
2 1
3 2
If the answer is to use things like "mysql variables", "views", etc, please kindly explain how those things work. Again, as a total mysql novice, "select", "join", "where", "group by" are all I know so far. I am eager to learn more but have trouble understanding more advanced mysql concepts so far. Many thanks in advance.
Using a bit more data than you posted, I found a query that might do what you need. It does use the variables as you predicted :) but I hope it's pretty self-explanatory. Let's start with the table:
CREATE TABLE a_free
(`Staff_ID` int, `Avail_Slots_Datetime` datetime)
;
INSERT INTO a_free
(`Staff_ID`, `Avail_Slots_Datetime`)
VALUES
(1, '2015-01-01 09:00:00'),
(1, '2015-01-01 10:00:00'),
(1, '2015-01-01 11:00:00'),
(1, '2015-01-01 13:00:00'),
(2, '2015-01-01 09:00:00'),
(2, '2015-01-01 10:00:00'),
(2, '2015-01-01 11:00:00'),
(3, '2015-01-01 09:00:00'),
(3, '2015-01-01 12:00:00'),
(3, '2015-01-01 15:00:00'),
(3, '2015-01-01 16:00:00'),
(3, '2015-01-01 17:00:00'),
(3, '2015-01-01 18:00:00')
;
Then there's a query to find the consecutive slots. It lists start times of each pair, and marks each group of consecutive slots with a unique number. The case expression is where the magic happens, see the comments:
select
Staff_ID,
Avail_Slots_Datetime as slot_start,
case
when #slot_group is null then #slot_group:=0 -- initalize the variable
when #prev_end <> Avail_Slots_Datetime then #slot_group:=#slot_group+1 -- iterate if previous slot end does not match current one's start
else #slot_group -- otherwise just just keep the value
end as slot_group,
#prev_end:= Avail_Slots_Datetime + interval 1 hour as slot_end -- store the current slot end to compare with next row
from a_free
order by Staff_ID, Avail_Slots_Datetime asc;
Having the list with slot groups identified, we can wrap the query above in another one to get the lengths of each slot group. The results of the first query are treated as any other table:
select
Staff_ID,
slot_group,
min(slot_start) as group_start,
max(slot_end) as group_end,
count(*) as group_length
from (
select
Staff_ID,
Avail_Slots_Datetime as slot_start,
case
when #slot_group is null then #slot_group:=0
when #prev_end <> Avail_Slots_Datetime then #slot_group:=#slot_group+1
else #slot_group
end as slot_group,
#prev_end:= Avail_Slots_Datetime + interval 1 hour as slot_end
from a_free
order by Staff_ID, Avail_Slots_Datetime asc
) groups
group by Staff_ID, slot_group;
Note: if you use the same DB connection to execute the query again, the variables would not be reset, so the slot_groups numbering will continue to grow. This normally should not be a problem, but to be on the safe side, you need to execute something like this before or after:
select #prev_end:=null;
Play with the fiddle if you like: http://sqlfiddle.com/#!2/0446c8/15
The query I'm using to sum is:
SELECT SUM(Amount) as `Amount`
FROM balances
WHERE User_ID='$user_id' AND `Wallet_ID` = '$idtw'
I need to combine all the rows so there is only one record in the database, because while sum works for display, when updating the balance upon a withdrawal it's practically impossible.
If I get it right, the user has n-balance records and you're interested in the sum of his balances:
SELECT SUM(balance) FROM balances WHERE user_id=? GROUP BY user_id;
Obviously I didn't get it right. Then I try to understand that your balance relation is a history record and you're only interested in the current balance (=last record). I would suggest to give your records versions and keep track which is the current record. A scheme like this might help:
CurrentBalance(user_id, symbol, version)
Balance(user_id, symbol, version, amount)
Your CurrentBalance gives you the latest version for a given symbol and user and you can easily track your balance records, read your current balance from Balance.
The content of your relations might look like this:
CurrentBalance = {
(1, "BTC", 3),
(2, "BTC", 4),
(1, "USD", 2),
}
Balance = {
(1, "BTC", 1, 1.123),
(1, "BTC", 2, 0),
(1, "BTC", 3, 4.234), // current balance for user 1 and symbol BTC
(2, "BTC", 1, 1.00),
(2, "BTC", 2, 1.11),
(2, "BTC", 3, 0),
(2, "BTC", 4, 5.123), // current balance for user 2 and symbol BTC
(1, "USD", 1, 11.11),
(1, "USD", 2, 10.12), // current balance for user 1 and symbol USD
}
When you're updating the current balance of a user you simply increase the version
for the user and his symbol in the CurrentBalance relation and add a new record
with that new version to the Balance relation.
Just make an ORDER BY id DESC LIMIT 1to grab the last record instead.
A good solution is every time someone "withdraws his remaining bitcoin" you, instead of update the credits to 0, you can, for example delete the row. So, the user have no rows on that table means 0 credits.
This is good only if you have only a very simple table without additional fields. (a table that saves only the user_id and the amount)