How to SELECT from table, when the condition is depending on rows - sql-server-2008

Help needed please.
I have a trouble with a select.
I have a table containing probation periods with following columns:
CREATE TABLE probation (
pk_person int,
p_startdate datetime,
p_enddate datetime,
PRIMARY KEY (pk_person )
)
and a table containing persons absences with following columns:
CREATE TABLE absences (
pk_person int,
a_startdate datetime,
a_enddate datetime,
)
Now, i need to calculate the end of the probation period for person. The problem is that the absences effectively prolongs the probation period.
For example:
insert into probation (pk_person, p_startdate, p_enddate) values (1, 01-01-2001, 31-03-2001)
insert into absences (pk_person, a_startdate, a_enddate) values (1, 01-02-2001, 28-02-2001)
insert into absences (pk_person, a_startdate, a_enddate) values (1, 01-04-2001, 10-04-2001)
Now i need to select the complete probation period for person with the following result:
pk_person, startdate, enddate
1 01-01-2001 08-05-2001
I could use a stored procedure, but can't do it for reasons.
I'm using MSSQL 2008.
Thanks!

Select
Int
Big list.Start date
Big list. End date
Datediff(day, big list.start date, big list.end date) as original duration
Sum(datediff(day, small list.start date, small list.end date) as total extra time
DateAdd(day, big list.start date,
Sum(datediff(day, small list.start date, small list.end date)) as new end date
From
Big list
Left outer join small list on big list.int = small list.int
Group by
Big list.everything

Related

Need to find Three most expensive path on average having code 'A' with delivery in May 2021

Statement:
Need to find Three most expensive path(from_to_ in table) on average that code (with_ in table) 'A' with delivery in May 2021? If two ties then include both.
Schema:
'observed_on', date,
'from_', varchar(3),
'to_', varchar(3),
'from_to_', varchar(8),
'with_', varchar(3),
'cart_no', varchar(8),
'deliver_on', date,
'd_charge', double,
Sample data:
click to view
Solution I tried:
SELECT
from_to_
,avg_price
FROM
(
SELECT
from_to_
,ROUND(AVG(d_charge),2) AS avg_price
,DENSE_RANK() OVER(ORDER BY ROUND(AVG(d_charge),2) DESC) rank_by_avgp
FROM
(
SELECT
*
FROM DELIVERY
WHERE deliver_on BETWEEN '2021-05-01' AND '2021-05-30'
AND with_ = 'A'
) AS A
GROUP BY from_to_
) AS bb
WHERE bb.rank_by_avgp <=3;
I know it's a workaround so I am looking for a better solution
#nishant, you have a quite poorly asked question here.
The issues with your question
The sample data, you reference, is a picture? Why not give text values or even better the DML statements to set the example up.
In the referenced dataset(picture) all the DELIVERY.deliver_on date values are before 2010. So the conditions you have here deliver_on BETWEEN '2021-05-01' AND '2021-05-30' will just not return anything for the example data.
If you say
I know it's a workaround so I am looking for a better solution
then based on what? It is a workaround for what? It looks to be producing correct results, so what is the problem with it? Do you want it to perform better or what?
You are not specifing the DB version. The different MySQL versions can have different solutions.
One possible solution
The example dataset setup:
DDL
CREATE TABLE DELIVERY (
observed_on DATE
, from_ VARCHAR(3)
, to_ VARCHAR(3)
, from_to_ VARCHAR(8)
, with_ VARCHAR(3)
, cart_no VARCHAR(8)
, deliver_on DATE
, d_charge DOUBLE
);
DML
INSERT INTO DELIVERY VALUES ('2012-01-19','Aus','Nzl','AusNzl','A','2118','2021-04-19',82.3);
INSERT INTO DELIVERY VALUES ('2012-01-19','Aus','Nzl','AusNzl','A','2118','2021-05-19',82.3);
INSERT INTO DELIVERY VALUES ('2012-01-19','Aus','Nzl','AusNzl','A','2118','2021-05-19',82.3);
INSERT INTO DELIVERY VALUES ('2013-01-19','Ind','Sla','IndSla','B','2233','2021-05-19',70.32);
INSERT INTO DELIVERY VALUES ('2013-01-19','Ind','Sla','IndSla','A','2233','2021-05-19',70.32);
INSERT INTO DELIVERY VALUES ('2013-01-19','Eur','Usa','EurUsa','C','2434','2021-05-19',67.53);
INSERT INTO DELIVERY VALUES ('2013-01-19','Eur','Usa','EurUsa','A','2434','2021-05-19',67.53);
INSERT INTO DELIVERY VALUES ('2013-01-19','Xyz','Usa','XyzUsa','A','2434','2021-05-19',67.53);
INSERT INTO DELIVERY VALUES ('2013-01-19','Xyz','Sla','XyzSla','A','2434','2021-05-19',67.51);
INSERT INTO DELIVERY VALUES ('2012-01-19','Aus','Nzl','AusNzl','A','2323','2021-05-19',82.3);
INSERT INTO DELIVERY VALUES ('2012-01-19','Aus','Nzl','AusNzl','A','2118','2021-06-19',82.3);
QUERY
SELECT from_to_
, avg_d_charge
, denserank_avg_d_charge
FROM /*SUB_to_calculate_the_denserank*/
(
SELECT from_to_
, ROUND(avg_d_charge, 2) AS avg_d_charge
, DENSE_RANK() OVER (
ORDER BY ROUND(avg_d_charge, 2) DESC
) denserank_avg_d_charge /*dense ranking*/
FROM /*SUB_to_calculate_the_averages*/
(
SELECT from_to_
, ROW_NUMBER() OVER (PARTITION BY from_to_) AS rownumber /*To filter for only one row per from_to_.*/
, AVG(d_charge) OVER (PARTITION BY from_to_) AS avg_d_charge /*Average caldulation*/
FROM /*DELIVERY*/
DELIVERY
WHERE 1 = 1
AND with_ = 'A' /* The "code" filter*/
AND DATE_SUB(deliver_on, INTERVAL DAYOFMONTH(deliver_on) - 1 DAY) = '2021-05-01' /* The 2021-05 filter*/
) SUB_to_calculate_the_averages
WHERE 1 = 1
AND rownumber = 1
) SUB_to_calculate_the_denserank
WHERE 1 = 1
AND denserank_avg_d_charge < 4;
The only main difference here from your solution is then that I do not use the aggegate GROUP BY here, only analytical functions. I prefer this quite often as it allows to carry later other attributes through the query without the need to apply the aggregate functions on them etc. But in the end then this comes down to performance and the requirements what/how should be done.

How to create calculation in MySQL between date and price

I'm want to calculate the date to count days and calculate with price or custom price in MySQL.
This is booking table
CREATE TABLE tbl_booking(
booking_id int(11) NOT NULL AUTO_INCREMENT,
user_id int(11),
comp_id int(11),
register_number varchar(100),
booking_status int(11),
pick_date date,
drop_date date,)
This is vehicle table
CREATE TABLE tbl_vehicle(
register_number varchar(100) NOT null,
vehicle_model varchar(255),
vehicle_type varchar(255),
vehicle_price varchar(10));
So, like this:
totalprice = (pick_date - drop_date) * vehicle_price
Join two tables on common column which seems to be register_number then use datediff to calculate the difference in days between two dates and multiply it by price to get total price for each booking:
select
b.*,
datediff(b.drop_date, b.pick_date) * v.price as total_price
from tbl_booking b
inner join tbl_vehicle v on
b.register_number = v.register_number
Remember that you should substract later date from an earlier one (drop - pick) instead of (pick - drop) since this will give you a negative number.
Use below :
DATEDIFF(pick_date , drop_date) * price
DATEDIFF will give you day different between two dates.
SELECT DATEDIFF("2017-06-25", "2017-06-15");
Output will be : 10
As you price is in another table hope you will be able to join and calculate.
You can convert the date field data into days, then subtract them, and multiply with price. Please note that the syntax can vary among the different implementations of SQL. With the correct MySQL syntax this would be:
SELECT (TO_DAYS(drop_date) - TO_DAYS(pick_date)) * price FROM tbl_booking;
Also note that according to your code, tbl_booking does not contain the price field, so you'll probably need to join with another table as well.

SSRS: Parameter population from a dataset

I have a dataset with start dates, end dates, and term codes. The user will enter in an academic year. The academic year will be used to determine the three term codes for that year. I want to use those three codes to select start and end dates for each term, which will go into the main query. I can achieve this by setting up three datasets that will hold the term code, start date, and end date for each term and populating the start and end date parameters from there, but what I want to know is if there is a more dynamic way to achieve this.
Thanks!
Some clarification. I want a way to populate all six parameters from one dataset, not each start and end date getting its own dataset.
I can't be sure of how your data is set up, so I'm going to make a few guesses here. You can return six separate dates by pivoting the Start and End Dates for each Term:
declare #TermCodes table(AcademicYear int,TermCode varchar(50))
insert into #TermCodes
values
(2014,'FL14'),
(2014,'SP14'),
(2014,'SM14')
declare #TermDates table(TermCode varchar(50), StartDate date,EndDate date)
insert into #TermDates
values
('FL14','20140915','20141215'),
('SP14','20150115','20150415'),
('SM14','20150515','20150815')
declare #AcademicYear int
set #AcademicYear = 2014
select
min(StartDate1) StartDate1,
min(EndDate1) EndDate1,
min(StartDate2) StartDate2,
min(EndDate2) EndDate2,
min(StartDate3) StartDate3,
min(EndDate3) EndDate3
from (
select
td.StartDate,
td.EndDate,
'StartDate' + cast(row_number() over(order by td.StartDate) as char(1)) StartDates,
'EndDate' + cast(row_number() over(order by td.StartDate) as char(1)) EndDates
from #TermCodes tc
inner join #TermDates td
on td.TermCode = tc.TermCode
where tc.AcademicYear = #AcademicYear
) t
pivot (
max(StartDate)
for StartDates in(StartDate1,StartDate2,StartDate3)
) sd
pivot (
max(EndDate)
for EndDates in(EndDate1,EndDate2,EndDate3)
) ed

Hotel Room Rates for different seasons

I have a database (MySQL) with a table containing date ranges (as startdate and enddate) and a rate field. The date range implies different seasons (low, high etc.). The scenario is such that a person checks in the hotel and his duration of stay is in two seasons. A sample data is like below:
SeasonName SartDate EndDate Rate
Low 01-01-2007 30-04-2007 100.00
High 01-05-2007 31-08-2007 150.00
Peak 01-09-2007 31-12-2007 200.00
The client's Check In Date is 29-04-2007 and Check Out Date is 03-05-2007. I need to calculate the exact number of nights for each season and also calculate the total amount.
The IDE is VB6. Any help will be extremely appreciated.
Thanks
Tom
Thanks for the response. I need the SQL to extract the information. As for the date validity, lets assume the rate applies till midnight (00:00). Hope i have clarified.
Tom
Having worked in a hotel and written the reservation system, hourly time is irrelevant as far
as billing goes. Everything is always charged by night. (Unless you plan to run a place that charges by the hour! ;-)) Check-in and check-out are operational considerations.
Do not use stored procuedures if you actually want to write a real reservation system.
It defeats the purpose of having a database.
Also, writing out dates like this is 2007-04-29 is really great way because not every one is from the same place and this is an international standard. Also notice, if you were to turn this into a string it will still be sorted correctly!
You need make a calandar table as MySQL does not have in built in functions to do it.
This procedure will build up dates for you.
drop table if exists calendar;
create table calendar
(
date_ date primary key
);
drop procedure fill_calendar;
delimiter $$
create procedure fill_calendar(start_date date, end_date date)
begin
declare date_ date;
set date_=start_date;
while date_ < end_date do
insert into calendar values(date_);
set date_ = adddate(date_, interval 1 day);
end while;
end $$
delimiter ;
call fill_calendar('2007-1-1', '2007-12-31');
from: http://www.ehow.com/how_7571744_mysql-calendar-tutorial.html
drop table if exists rates;
create table rates
(
season varchar(100) primary key,
start_date date references calendar(date_),
end_date date references calendar(date_),
rate float
);
insert into rates values ('Low', '2007-01-01', '2007-04-30', 100.00);
insert into rates values ('High', '2007-05-01', '2007-08-31', 150.00);
insert into rates values ('Peak', '2007-09-01', '2007-12-21', 200.00);
select * from rates;
season start_date end_date rate
Low 2007-01-01 2007-04-30 100
High 2007-05-01 2007-08-31 150
Peak 2007-09-01 2007-12-21 200
I'm going to ignore the dates you have given in your question and the assume the client is not travelling backwards in time.
select
date_, rate
from calendar
join rates
on date_ >= start_date and date_ <= end_date
where date_ between '2007-04-29' and '2007-5-01'
;
date_ rate
2007-04-29 100
2007-04-30 100
2007-05-01 150
select
sum(rate)
from calendar
join rates
on date_ >= start_date and date_ <= end_date
where date_ between '2007-04-29' and '2007-5-01'
sum(rate)
350
And, as you can see the sql is quite concise and readable without resorting to functions or procedures. This will be able to scale properly and handle more complex questions. Also, it enables referential checking to be used since the data is table based.
I used the DATEDIFF function to know the number of days between 2 dates. But since this function returns the number of days excluding the last day (e.g. DATEDIFF(d, '2007-04-29', '2007-04-30') return 1 instead of 2), I used a rate table like this:
SeasonName StartDate EndDate Rate
Low 01-01-2007 01-05-2007 100.00
High 01-05-2007 01-09-2007 150.00
Peak 01-09-2007 01-01-2008 200.00
This is the query I used. The inner of the two SELECTs calculates an effective end date for the customer's stay according to the season (either the end date of the season or it's date prior to it's check out date).
I used 02-05-2007 as the customer's stay end date instead of 03-05-2007 since usually a customer does does not pay for the day he checks out.
SELECT SeasonName, DATEDIFF(d, '2007-04-29', EffectiveEndDate) as NumberOfDays, Rate
FROM (
SELECT SeasonName,
CASE
WHEN EndDate < '2007-05-02' THEN EndDate
ELSE '2007-05-02'
END AS EffectiveEndDate,
Rate
FROM HotelRate
WHERE (StartDate <= '2007-04-29' and EndDate > '2007-04-29')
or (StartDate <= '2007-05-02' and EndDate > '2007-05-02')
) as SubSelect
This gives me this result:
SeasonName NumberOfDays Rate
Low 2 100.00
High 3 150.00
I hope it helps :)
Disclaimer: This is not the most efficient one, but it's the more clear, and if you have the price list cached in an array, the performance hit will be meaningless.
Dim numberOfDays As Integer, i As Integer
Dim CheckInDate As Date, CheckOutDate As Date, CurrDate As Date
Dim TotalRate As Currency
TotalRate = 0
CheckInDate = DateSerial(2007, 4, 29)
CheckOutDate = DateSerial(2007, 5, 3)
''// -1 asumming the last day is checkout day
numberOfDays = DateDiff("d", CheckInDate, CheckOutDate) - 1
For i = 0 To numberOfDays
CurrDate = DateAdd("d", i, CheckInDate)
TotalRate = TotalRate + CalculateRateForDay(CurrDate)
Next
Use an auxiliary calendar table:
SELECT S1.client_ID, H1.Rate, C1.dt
FROM HotelRates AS H1
INNER JOIN Calendar AS C1
ON C1.dt BETWEEN H1.SartDate AND H1.EndDate
INNER JOIN Stays AS S1
ON C1.dt >= check_in_date AND
C1.dt < check_out_date;

SQL query that returns all dates not used in a table

So lets say I have some records that look like:
2011-01-01 Cat
2011-01-02 Dog
2011-01-04 Horse
2011-01-06 Lion
How can I construct a query that will return 2011-01-03 and 2011-01-05, ie the unused dates. I postdate blogs into the future and I want a query that will show me the days I don't have anything posted yet. It would look from the current date to 2 weeks into the future.
Update:
I am not too excited about building a permanent table of dates. After thinking about it though it seems like the solution might be to make a small stored procedure that creates a temp table. Something like:
CREATE PROCEDURE MISSING_DATES()
BEGIN
CREATE TABLE TEMPORARY DATES (FUTURE DATETIME NULL)
INSERT INTO DATES (FUTURE) VALUES (CURDATE())
INSERT INTO DATES (FUTURE) VALUES (ADDDATE(CURDATE(), INTERVAL 1 DAY))
...
INSERT INTO DATES (FUTURE) VALUES (ADDDATE(CURDATE(), INTERVAL 14 DAY))
SELECT FUTURE FROM DATES WHERE FUTURE NOT IN (SELECT POSTDATE FROM POSTS)
DROP TABLE TEMPORARY DATES
END
I guess it just isn't possible to select the absence of data.
You're right — SQL does not make it easy to identify missing data. The usual technique is to join your sequence (with gaps) against a complete sequence, and select those elements in the latter sequence without a corresponding partner in your data.
So, #BenHoffstein's suggestion to maintain a permanent date table is a good one.
Short of that, you can dynamically create that date range with an integers table. Assuming the integers table has a column i with numbers at least 0 – 13, and that your table has its date column named datestamp:
SELECT candidate_date AS missing
FROM (SELECT CURRENT_DATE + INTERVAL i DAY AS candidate_date
FROM integers
WHERE i < 14) AS next_two_weeks
LEFT JOIN my_table ON candidate_date = datestamp
WHERE datestamp is NULL;
One solution would be to create a separate table with one column to hold all dates from now until eternity (or whenever you expect to stop blogging). For example:
CREATE TABLE Dates (dt DATE);
INSERT INTO Dates VALUES ('2011-01-01');
INSERT INTO Dates VALUES ('2011-01-02');
...etc...
INSERT INTO Dates VALUES ('2099-12-31');
Once this reference table is set up, you can simply outer join to determine the unused dates like so:
SELECT d.dt
FROM Dates d LEFT JOIN Blogs b ON d.dt = b.dt
WHERE b.dt IS NULL
If you want to limit the search to two weeks in the future, you could add this to the WHERE clause:
AND d.dt BETWEEN NOW() AND ADDDATE(NOW(), INTERVAL 14 DAY)
The way to extract rows from the mysql database is via SELECT. Thus you cannot select rows that do not exist.
What I would do is fill my blog table with all possible dates (for a year, then repeat the process)
create table blog (
thedate date not null,
thetext text null,
primary key (thedate));
doing a loop to create all dates entries for 2011 (using a program, eg $mydate is the date you want to insert)
insert IGNORE into blog (thedate,thetext) values ($mydate, null);
(the IGNORE keyword to not create an error (thedate is a primary key) if thedate exists already).
Then you insert the values normally
insert into blog (thedate,thetext) values ($mydate, "newtext")
on duplicate key update thetext="newtext";
Finally to select empty entries, you just have to
select thedate from blog where thetext is null;
You probably not going to like this:
select '2011-01-03', count(*) from TABLE where postdate='2011-01-03'
having count(*)=0 union
select '2011-01-04', count(*) from TABLE where postdate='2011-01-04'
having count(*)=0 union
select '2011-01-05', count(*) from TABLE where postdate='2011-01-05'
having count(*)=0 union
... repeat for 2 weeks
OR
create a table with all days in 2011, then do a left join, like
select a.days_2011
from all_days_2011
left join TABLE on a.days_2011=TABLE.postdate
where a.days_2011 between date(now()) and date(date_add(now(), interval 2 week))
and TABLE.postdate is null;