How to write sql query for the following - mysql

table - fu_plan_user (id_plan_user,id_user, id_plan, valid_from, valid_to, ...)
table - fu_user (id_user, name, company....)
table - fu_plan (id_plan, name, duration, ...)
This is the table structure somewhat.
I want to produce a list of accounts that have a close(valid_to) date which is the end of a month(30th day) and this date should be in the future, like grater than todays date (NOW()). The name of the company should not contain "trial" word.
The result should contain the following fields
id_user, name (from table fu_plan), company (from table fu_user), valid_to (from table fu_plan_user)
Something like this
Raw Query (not correct)
SELECT usr.id_user, payplan.name, usr.foretag, planUser.da_valid_to
from fu_plan_user planUser, fu_user usr, fu_plan payplan left join
fu_user usr
on planUser.id_user=usr.id_user
where planUser.da_valid_to > now() and
planUser.da_valid_to >= DATEADD(d, -30, getdate()) ;

After your explanation and only if your database is MySQL, i suggest to use this query:
SELECT user.id_user, plan.name, user.company, pbyu.valid_to
FROM fu_plan_user AS fbyu
JOIN fu_user AS user
ON user.id_user = fbyu.id_user
JOIN fu_plan AS plan
ON plan.id_plan = fbyu.id_plan
WHERE pbyu.valid_to > NOW()
AND LAST_DAY(pbyu.valid_to) = pbyu.valid_to
AND user.company NOT LIKE '%trial%';
If company isn't only lowercase values, you should use LOWER().
LAST_DAY() function will get the last day from month of valid_to date.

Related

Conditional SELECT SQL statements in MySQL

These 3 tables are set as follows in a MySQL database (not all columns are shown):
events_table
id
event_date
event_type // Working day or holiday
occasion // If event_type = 'holiday', occasion of holiday is entered here
classes_table
id
class_type
teacher_id (foreign key. Reference = events_table.id)
teachers_table
id
name
I want to write a SQL query wherein I want to select data depending on the data in the events_table.event_type on a particular date i.e., if the event_type on the event_date(let's say 25th Dec) is holiday then SELECT events_table.occasion... else if event_type on the event_date(let's say 28th Aug) is working_day then SELECT classes_table.id, classes_table.class_type
IF events_table.event_type <> 'holiday' THEN
-- If event on a particular day was not a holiday, select the following
SELECT
classes_table.id,
classes_table.class_type
FROM
classes_table
WHERE
teachers_table.username = 'teacher_name'
INNER JOIN teachers_table ON teachers_table.id = classes_table.teacher_id
ELSE
-- Else, select the following
SELECT
events_table.occasion
FROM
events_table
END IF
However as per this answer it is not possible. Also we cannot use IF...ELSE without BEGIN....END blocks, however doing that too doesn't help me. Is there any way we can do this in MySQL (preferably without sub-queries), if not what would be the most efficacious way of implementing the same?

Mysql checking . record for one day against record in different date 'Same-Store Sales'

Same-Store Sales concept if where you check how store performing today against same store yesterday to show if revenue grow or decrease.
so I have table with 5+ millions of records structured like
store_id , stats_date, trans_cnt (number of transactions),
revenue, time_period(week, day , year)
is there a way to avoid using cursor, to check if store_id record exist yesterday day and today and see if revenue goes up or down?
It can be achieved join on same filter data from table or sub table .
ie
select tdate.store_id ,(tdate.revenue - ydate.revenue) as diffrence
from (select store_id ,revenue from tablename where stats_date =getdata()) tdate
join ( select store_id ,revenue from tablename where stats_date = DATEADD(day, -1,getdata()) ) as ydate
on tdate.store_id = ydate.store_id
Note:
ydate filter data for yesterday
tdate filter data for yesterday
More filter condition can be added .
Or you are looking for
select tdate.store_id ,(tdate.revenue - ydate.revenue) as diffrence
from tablename tdate
join tablename as ydate
on tdate.store_id = ydate.store_id
and tdate.stats_date =ydate.DATEADD(day, -1,stats_date)

MySQL query to select all hostels with at least X spaces between start and end dates?

I have 2 tables, one with hostels (effectively a single-room hotel with lots of beds), and the other with bookings.
Hostel table: unique ID, total_spaces
Bookings table: start_date, end_date, num_guests, hostel_ID
I need a (My)SQL query to generate a list of all hostels that have at least num_guests free spaces between start_date and end_date.
Logical breakdown of what I'm trying to achieve:
For each hostel:
Get all bookings that overlap start_date and end_date
For each day between start_date and end_date, sum the total bookings for that day (taking into account num_guests for each booking) and compare with total_spaces, ensuring that there are at least num_guests spaces free on that day (if there aren't on any day then that hostel can be discounted from the results list)
Any suggestions on a query that would do this please? (I can modify the tables if necessary)
I built an example for you here, with more comments, which you can test out:
http://sqlfiddle.com/#!9/10219/9
What's probably tricky for you is to join ranges of overlapping dates. The way I would approach this problem is with a DATES table. It's kind of like a tally table, but for dates. If you join to the DATES table, you basically break down all the booking ranges into bookings for individual dates, and then you can filter and sum them all back up to the particular date range you care about. Helpful code for populating a DATES table can be found here: Get a list of dates between two dates and that's what I used in my example.
Other than that, the query basically follows the logical steps you've already outlined.
Ok, if you are using mysql 8.0.2 and above, then you can use window functions. In such case you can use the solution bellow. This solution does not need to compute the number of quests for each day in the query interval, but only focuses on days when there is some change in the number of hostel guests. Therefore, there is no helping table with dates.
with query as
(
select * from bookings where end_date > '2017-01-02' and start_date < '2017-01-05'
)
select hostel.*, bookingsSum.intervalMax
from hostel
join
(
select tmax.id, max(tmax.intervalCount) intervalMax
from
(
select hostel.id, t.dat, sum(coalesce(sum(t.gn),0)) over (partition by t.id order by t.dat) intervalCount
from hostel
left join
(
select id, start_date dat, guest_num as gn from query
union all
select id, end_date dat, -1 * guest_num as gn from query
) t on hostel.id = t.id
group by hostel.id, t.dat
) tmax
group by tmax.id
) bookingsSum on hostel.id = bookingsSum.id and hostel.total_spaces >= bookingsSum.intervalMax + <num_of_people_you_want_accomodate>
demo
It uses a simple trick, where each start_date represents +guest_num to the overall number of quests and each 'end_date' represents -guest_num to the overall number of quests. We than do the necessary sumarizations in order to find peak number of quests (intervalMax) in the query interval.
You change '2017-01-05' in my query to '2017-01-06' (then only two hostels are in the result) and if you use '2017-01-07' then just hostel id 3 is in the result, since it does not have any bookings yet.

Generating complex sql tables

I currently have an employee logging sql table that has 3 columns
fromState: String,
toState: String,
timestamp: DateTime
fromState is either In or Out. In means employee came in and Out means employee went out. Each row can only transition from In to Out or Out to In.
I'd like to generate a temporary table in sql to keep track during a given hour (hour by hour), how many employees are there in the company. Aka, resulting table has columns HourBucket, NumEmployees.
In non-SQL code I can do this by initializing the numEmployees as 0 and go through the table row by row (sorted by timestamp) and add (employee came in) or subtract (went out) to numEmployees (bucketed by timestamp hour).
I'm clueless as how to do this in SQL. Any clues?
Use a COUNT ... GROUP BY query. Can't see what you're using toState from your description though! Also, assuming you have an employeeID field.
E.g.
SELECT fromState AS 'Status', COUNT(*) AS 'Number'
FROM StaffinBuildingTable
INNER JOIN (SELECT employeeID AS 'empID', MAX(timestamp) AS 'latest' FROM StaffinBuildingTable GROUP BY employeeID) AS LastEntry ON StaffinBuildingTable.employeeID = LastEntry.empID
GROUP BY fromState
The LastEntry subquery will produce a list of employeeIDs limited to the last timestamp for each employee.
The INNER JOIN will limit the main table to just the employeeIDs that match both sides.
The outer GROUP BY produces the count.
SELECT HOUR(SBT.timestamp) AS 'Hour', SBT.fromState AS 'Status', COUNT(*) AS 'Number'
FROM StaffinBuildingTable AS SBT
INNER JOIN (
SELECT SBIJ.employeeID AS 'empID', MAX(timestamp) AS 'latest'
FROM StaffinBuildingTable AS SBIJ
WHERE DATE(SBIJ.timestamp) = CURDATE()
GROUP BY SBIJ.employeeID) AS LastEntry ON SBT.employeeID = LastEntry.empID
GROUP BY SBT.fromState, HOUR(SBT.timestamp)
Replace CURDATE() with whatever date you are interested in.
Note this is non-optimal as it calculates the HOUR twice - once for the data and once for the group.
Again you are using the INNER JOIN to limit the number of returned row, this time to the last timestamp on a given day.
To me your description of the FromState and ToState seem the wrong way round, I'd expect to doing this based on the ToState. But assuming I'm wrong on that the following should point you in the right direction:
First, I create a "Numbers" table containing 24 rows one for each hour of the day:
create table tblHours
(Number int);
insert into tblHours values
(0),(1),(2),(3),(4),(5),(6),(7),
(8),(9),(10),(11),(12),(13),(14),(15),
(16),(17),(18),(19),(20),(21),(22),(23);
Then for each date in your employee logging table, I create a row in another new table to contain your counts:
create table tblDailyHours
(
HourBucket datetime,
NumEmployees int
);
insert into tblDailyHours (HourBucket, NumEmployees)
select distinct
date_add(date(t.timeStamp), interval h.Number HOUR) as HourBucket,
0 as NumEmployees
from
tblEmployeeLogging t
CROSS JOIN tblHours h;
Then I update this table to contain all the relevant counts:
update tblDailyHours h
join
(select
h2.HourBucket,
sum(case when el.fromState = 'In' then 1 else -1 end) as cnt
from
tblDailyHours h2
join tblEmployeeLogging el on
h2.HourBucket >= el.timeStamp
group by h2.HourBucket
) cnt ON
h.HourBucket = cnt.HourBucket
set NumEmployees = cnt.cnt;
You can now retrieve the counts with
select *
from tblDailyHours
order by HourBucket;
The counts give the number on site at each of the times displayed, if you want during the hour in question, we'd need to tweak this a little.
There is a working version of this code (using not very realistic data in the logging table) here: rextester.com/DYOR23344
Original Answer (Based on a single over all count)
If you're happy to search over all rows, and want the current "head count" you can use this:
select
sum(case when t.FromState = 'In' then 1 else -1) as Heads
from
MyTable t
But if you know that there will always be no-one there at midnight, you can add a where clause to prevent it looking at more rows than it needs to:
where
date(t.timestamp) = curdate()
Again, on the assumption that the head count reaches zero at midnight, you can generalise that method to get a headcount at any time as follows:
where
date(t.timestamp) = "CENSUS DATE" AND
t.timestamp <= "CENSUS DATETIME"
Obviously you'd need to replace my quoted strings with code which returned the date and datetime of interest. If the headcount doesn't return to zero at midnight, you can achieve the same by removing the first line of the where clause.

how can write the sql query for the following records

table - fu_plan_user (id_plan_user,id_user, id_plan, valid_from, valid_to, ...)
table - fu_user (id_user, name, company....)
table - fu_plan (id_plan, name, duration, ...)
I want to produce a list of accounts that have a close(valid_to) date which is the end of a month and this date should be in the future, like grater than todays date (NOW()). The name should not contain "trial" word.
The list should include:
id_user
payplan name (fu_plan->name)
customer name (fu_user->name)
end date (fu_plan_user->valid_to)
I have tried this so far but no success
SELECT *
from fu_plan_user
where da_valid_to > now()
and da_valid_to > DATEADD(day, 30, GETDATE());
How can i produce this list of records with MySQL query?