I have a hospital database which looks something like this:
id | patient_name | admitDate | DischargeDate
1 | john | 3/01/2011 08:50 | 5/01/2011 12:50
2 | lisa | 3/01/2011 09:50 | 4/01/2011 13:50
3 | ron | 5/01/2012 10:40 | 10/01/2012 03:50
4 | howard | 6/02/2013 08:05 | 10/02/2013 08:50
5 | john | 6/02/2013 12:04 | 7/02/2013 01:50
The admitDate is same for many entries (time may be different). I want to find out how many patients were admitted on any particular day so if I do this:
select distinct left(admitDate,10),
(select count(distinct left(admitDate,10) ) from hospital)
from hospital
I get output as all distinct admit dates in 1st column and same value 5 in all rows of second column. How do I make it so that only corresponding repetition count is found in 2nd column and not the count of entire admitDate set.
datatype of admitdate is varchar(50)
I am using left function because I only have to find out uniqueness in dates not in time.
Expected result:
admitDate | Count
3/01/2011 | 2
5/01/2012 | 1
6/02/2013 | 2
Current result:
admitDate | Count
3/01/2011 | 5
5/01/2012 | 5
6/02/2013 | 5
If your admitDate Column has Time too, you need use Convert() function to eliminate the time to group by your data per each day:
Select CONVERT(date, admitDate), count(*)
from hospital
group by CONVERT(date, admitDate);
If you use Varchar instead of Date data type for your admitDate column you can try this:
SELECT LEFT(admitDate, charindex(' ', admitDate) - 1) as ADMITDATE , count(*) as COUNTER
from hospital
group by LEFT(admitDate, charindex(' ', admitDate) - 1) ;
or:
SELECT convert(date, (convert(datetime2, admitDate,103)) ), count(*)
from hospital
group by convert(date, (convert(datetime2, admitDate,103)) )
http://www.sqlfiddle.com/#!18/c913b/1
CREATE TABLE Table1 (
[ID] int PRIMARY KEY,
[Date] date,
[Name] varchar(40)
);
INSERT INTO Table1 (ID, date, Name )
Values ('1','07-20-18','Fred'),
('2','07-15-18','Sam'),
('3','07-20-18','Ben'),
('4','07-19-18','Simon'),
('5','07-25-18','Dave');
Current Query:
DECLARE #StartDate AS DATETIME,
#CurrentDate AS DATETIME
SET #StartDate = GETDATE() -30
SET #CurrentDate = GETDATE()
SELECT [ID], [Date], [Name]
FROM Table1
WHERE [Date] BETWEEN #StartDate AND #CurrentDate
Current result:
| ID | Date | Name |
|----|------------|-------|
| 1 | 2018-07-20 | Fred |
| 2 | 2018-07-15 | Sam |
| 3 | 2018-07-20 | Ben |
| 4 | 2018-07-19 | Simon |
This query compares with the range of day set for the #startdate variable. This can be changed to anything by anyone who wants to run the query. The issue is if someone selects a really large range of dates it crashes. Is there a way i can set an if statement that throws an error message if for example they tried to run a query for longer than 100 days.
Result: ERROR - Please lower the date range
Is this possible at all?
In my database I have a Reservation table and it has three columns Initial Day, Last Day and the House Id.
I want to count the total days and omit those who are repeated, for example:
+-------------+------------+------------+
| | Results | |
+-------------+------------+------------+
| House Id | InitialDay | LastDay |
+-------------+------------+------------+
| 1 | 2017-09-18 | 2017-09-20 |
| 1 | 2017-09-18 | 2017-09-22 |
| 19 | 2017-09-18 | 2017-09-22 |
| 20 | 2017-09-18 | 2017-09-22 |
+-------------+------------+------------+
If you noticed the House Id with the number 1 has two rows, and each row has dates but the first row is in the interval of dates of the second row. In total the number of days should be 5 because the first shouldn't be counted as those days already exist in the second.
The reason why this is happening is that each house has two rooms, and different persons can stay in that house on the same dates.
My question is: how can I omit those cases, and only count the real days the house was occupied?
In your are using SQL Server 2012 or higher you can use LAG() to get the previous final date and adjust the initial date:
with ReservationAdjusted as (
select *,
lag(LastDay) over(partition by HouseID order by InitialDay, LastDay) as PreviousLast
from Reservation
)
select HouseId,
sum(case when PreviousLast>LastDay then 0 -- fully contained in the previous reservation
when PreviousLast>=InitialDay then datediff(day,PreviousLast,LastDay) -- overlap
else datediff(day,InitialDay,LastDay)+1 -- no overlap
end) as Days
from ReservationAdjusted
group by HouseId
The cases are:
The reservation is fully included in the previous reservation: we only need to compare end dates because the previous row is obtained ordering by InitialDay, LastDay, so the previous start date is always minor or equal than the current start date.
The current reservation overlaps with the previous: in this case we adjust the start and don't add 1 (the initial day is already counted), this case include when the previous end is equal to the current start (is a one day overlap).
There is no overlap: we just calculate the difference and add 1 to count also the initial day.
Note that we don't need extra condition for the reservation of a HouseID because by default the LAG() function returns NULL when there isn't a previous row, and comparisons with null always are false.
Sample input and output:
| HouseId | InitialDay | LastDay |
|---------|------------|------------|
| 1 | 2017-09-18 | 2017-09-20 |
| 1 | 2017-09-18 | 2017-09-22 |
| 1 | 2017-09-21 | 2017-09-22 |
| 19 | 2017-09-18 | 2017-09-27 |
| 19 | 2017-09-24 | 2017-09-26 |
| 19 | 2017-09-29 | 2017-09-30 |
| 20 | 2017-09-19 | 2017-09-22 |
| 20 | 2017-09-22 | 2017-09-26 |
| 20 | 2017-09-24 | 2017-09-27 |
| HouseId | Days |
|---------|------|
| 1 | 5 |
| 19 | 12 |
| 20 | 9 |
select house_id,min(initialDay),max(LastDay)
group by houseId
If I understood correctly!
Try out and let me know how it works out for you.
Ted.
While thinking through your question I came across the wonder that is the idea of a Calendar table. You'd use this code to create one, with whatever range of dates your want for your calendar. Code is from http://blog.jontav.com/post/9380766884/calendar-tables-are-incredibly-useful-in-sql
declare #start_dt as date = '1/1/2010';
declare #end_dt as date = '1/1/2020';
declare #dates as table (
date_id date primary key,
date_year smallint,
date_month tinyint,
date_day tinyint,
weekday_id tinyint,
weekday_nm varchar(10),
month_nm varchar(10),
day_of_year smallint,
quarter_id tinyint,
first_day_of_month date,
last_day_of_month date,
start_dts datetime,
end_dts datetime
)
while #start_dt < #end_dt
begin
insert into #dates(
date_id, date_year, date_month, date_day,
weekday_id, weekday_nm, month_nm, day_of_year, quarter_id,
first_day_of_month, last_day_of_month,
start_dts, end_dts
)
values(
#start_dt, year(#start_dt), month(#start_dt), day(#start_dt),
datepart(weekday, #start_dt), datename(weekday, #start_dt), datename(month, #start_dt), datepart(dayofyear, #start_dt), datepart(quarter, #start_dt),
dateadd(day,-(day(#start_dt)-1),#start_dt), dateadd(day,-(day(dateadd(month,1,#start_dt))),dateadd(month,1,#start_dt)),
cast(#start_dt as datetime), dateadd(second,-1,cast(dateadd(day, 1, #start_dt) as datetime))
)
set #start_dt = dateadd(day, 1, #start_dt)
end
select *
into Calendar
from #dates
Once you have a calendar table your query is as simple as:
select distinct t.House_id, c.date_id
from Reservation as r
inner join Calendar as c
on
c.date_id >= r.InitialDay
and c.date_id <= r.LastDay
Which gives you a row for each unique day each room was occupied. If you need a sum of how many days each room was occupied it becomes:
select a.House_id, count(a.House_id) as Days_occupied
from
(select distinct t.House_id, c.date_id
from so_test as t
inner join Calendar as c
on
c.date_id >= t.InitialDay
and c.date_id <= t.LastDay) as a
group by a.House_id
Create a table of all the possible dates and then join it to the Reservations table so that you have a list of all days between InitialDay and LastDay. Like this:
DECLARE #i date
DECLARE #last date
CREATE TABLE #temp (Date date)
SELECT #i = MIN(Date) FROM Reservations
SELECT #last = MAX(Date) FROM Reservations
WHILE #i <= #last
BEGIN
INSERT INTO #temp VALUES(#i)
SET #i = DATEADD(day, 1, #i)
END
SELECT HouseID, COUNT(*) FROM
(
SELECT DISTINCT HouseID, Date FROM Reservation
LEFT JOIN #temp
ON Reservation.InitialDay <= #temp.Date
AND Reservation.LastDay >= #temp.Date
) AS a
GROUP BY HouseID
DROP TABLE #temp
I'm having trouble with getting the records for the following
| DATEFROM | DATETO
| 2012-01-02 | 2012-01-03
| 2012-01-11 | 2012-01-16
| 2012-01-08 | 2012-01-22
| 2012-01-29 | 2012-01-30
| 2012-01-08 | 2012-01-11
I'm trying to get count of ranges containing the day for each day from beginning of first range ending last date of last range.
Sample output:
2012-01-02 | 1
2012-01-03 | 1
2012-01-08 | 2
2012-01-09 | 2
2012-01-10 | 2
2012-01-11 | 3
2012-01-12 | 2
2012-01-13 | 2
2012-01-14 | 2
2012-01-15 | 2
2012-01-16 | 2
......
My database contains data from 2008 to nowadays.
In other words I am trying to get how many times a record is found for a specific date.
Not every day is in the TABLE for each month
I found this post Tricky mysql count occurrences of each day within date range but can't convert the code provided to my SQL Server 2012.
You could try here
http://sqlfiddle.com/#!6/0855b/1
Ok, this is one way to do what you want:
DECLARE #MinDate DATE, #MaxDate DATE;
SELECT #MinDate = MIN(DATEFROM),
#MaxDate = MAX(DATETO)
FROM ENTRIES;
WITH Dates AS
(
SELECT DATEADD(DAY,number,#MinDate) [Date]
FROM master.dbo.spt_values
WHERE type = 'P'
AND number > 0
AND DATEADD(DAY,number,#MinDate) <= #MaxDate
)
SELECT A.[Date],
COUNT(*) N
FROM Dates A
LEFT JOIN Entries B
ON A.[Date] >= B.DATEFROM
AND A.[Date] <= B.DATETO
GROUP BY A.[Date]
ORDER BY A.[Date]
If the range dates is over 2047 days, then you'll need to create more values than the ones that are available in master.dbo.spt_values (this is trivial, for instance you can use a CROSS JOIN).
Here is the sqlfiddle for you to try.
I'm trying to compile some lifetime value information for customers within one of our databases.
We have an MS SQL Server database which stores all of our customer/transactional information.
My issue is that I don't have much experience when it comes to MS SQL Server (or SQL in general) - I'd like to be able to run a query against the database that pulls AVG number of loans, and AVG revenue based on three criteria:
1.) Loans be counted if they are 'approved'
2.) Loans from a customer_id only be counted if the first loan (first identified by date_created field) be on or after a certain 'mm/yyyy'
3.) I'm able to specify for how many months after the first 'mm/yyyy' to tally the number of loans / revenue to be included within the AVG
Here is what the database would look like:
customer_id | loan_status | date_created | revenue
111 | 'approved' | 2010-06-20 17:17:09 | 100.00
222 | 'approved' | 2010-06-21 09:54:43 | 255.12
333 | 'denied' | 2011-06-21 12:47:30 | NULL
333 | 'approved' | 2011-06-21 12:47:20 | 56.87
222 | 'denied' | 2011-06-21 09:54:48 | NULL
222 | 'approved' | 2011-06-21 09:54:18 | 50.00
111 | 'approved' | 2011-06-20 17:17:23 | 100.00
... loads' of records ...
555 | 'approved' | 2012-01-02 09:08:42 | 24.70
111 | 'denied' | 2012-01-05 02:10:36 | NULL
666 | 'denied' | 2012-02-05 03:31:16 | NULL
555 | 'approved' | 2012-02-17 09:32:26 | 197.10
777 | 'approved' | 2012-04-03 18:28:45 | 300.50
777 | 'approved' | 2012-06-28 02:42:01 | 201.80
555 | 'approved' | 2012-06-21 22:16:59 | 10.00
666 | 'approved' | 2012-09-30 01:17:20 | 50.00
If I wanted to find the avg transaction count (approved transactions), and average revenue per approved transaction for all customer's who's first loan was in/after 2012-01, and for a period of 4 months after then, how would I go about querying the database?
Any help is greatly appreciated.
something like this (there maybe a few typos here and there)...
you could first calculate the minimum loan date:
select customer_id, min(date_created) from table t where loan_status = 'approved' group by customer_id
then you can join to it:
select customer_id, count(date_created), avg(revenue) from table t
join (
select customer_id, min(date_created) as min_date from table t where loan_status = 'approved' group by customer_id ) s
on t.customer_id = s.customer_id
where t.date_created between s.min_date and DATEADD(month, 4, s.min_date) and t.loan_status = 'approved'
Rename tbl to your table name.
Specify dates in the format YYYYMMDD.
select customer_id, AVG(revenue) average_revenue
from
(
select customer_id
from tbl
group by customer_id
having min(date_created) >= '20120101'
) fl
join tbl t on t.customer_id = fl.customer_id
where t.loan_status = 'approved'
and date_created < '20120501' -- NOT including May the first, so Jan through Apr (4 months)
If you mean 4 months after each customer's first loan, leave me a comment, state whether it's 4 calendar months (e.g. 15-Jan to 15-May) or up to the last day of the 4th month (15-Jan to 30-Apr), and I'll update the answer.