Selecting values in a table not in a time range - sql

I have two tables (simplified version):
create table Schedule(
Id int,
ScheduledData datetime,
UserId int)
create table User(
Id int,
Name varchar(50))
In the first table I store all scheduled meetings , linking it to an user.
What I want to is retrieve all the free times an user has. It doesn't have to be very detailed.
For example, if a user doesn't have a meeting scheduled for 07/02/2014 morning (earlier than 12:59:59), display a row with the user's name and date. The same if he has a free afternoon.
What I've tried so far and didn't work is to create a temporary table and fill it with all the days of the month and all the users in my DB. That worked well using a CTE:
create table #Temp(
StartData datetime,
EndDate datetim,
UserId int)
Then, I did this to display the rows:
select U.Name, X.ScheduledDate
from #Temp T
left outer join
(select S.UserId
from Schedules S
where ScheduledData between #X and #Y) X on T.UserId = S.UserId
left outer join User U on T.UserId = U.Id
where S.ScheduledDate between T.StartDate and T.EndDate
It didn't work well and I can't make sense of it. I've struggling with this all day and this is the best I've got so far.

The problem could use a little more specification:
What does a row in 'Schedule' mean? The 'ScheduledData' column is just one instant in time; is this the start time of the meeting? How long is the meeting?
What does it mean to display "all of a user's free times?" Time is a continuous (well, sort of) quantity. How should the result be represented?
The spirit of your example seems to be, given a block of time, return a true/false value on whether a meeting occurs in that time. Let's say you have a table called 'Users' containing each user's ID. How about creating a table 'time_segments' with three columns 'id', 'start_time' and 'end_time'; you could define any time segment you want for example your 'morning' block, and then fetch the free times with something like:
select UserId,all_slots.id
from
( select
TimeSegments.id, UserId
from
TimeSegments
cross join Users ) all_slots
left outer join
( select
TimeSegments.id, UserId
from
Schedule
cross join TimeSegments
where
Schedule.datetime between TimeSegments.start_time and TimeSegments.end_time
) booked using(id, UserId)
where
booked.UserID is null

Related

MS Access VBA Select query

So first sorry for my english, my native language is german.
I have an ACCESS DB with a few Tables, the DB is filled with healing Plants, and there is one Table named "Issues" it looks like that:
Columns: ID -- Name -- Headache -- Pain -- Vomitting - and so on....
And the Columns for the indications like headache and so on, are boolean- True or False.
Now i`d like to make an query that asks the user (With a listbox in a form or so, or a Text input) to tell his indication, and then there should be a list of Substances/Plants where the Value for the indication (ColumnName) is true.
I think thats a parameter for a search in a table for columns.
I'd look at the design of your database. Having a table with separate columns for each issue would be a real headache to update if another issue became apparent.
I'd probably use four tables for this:
Users: UserID (AutoNum, PK), UserName (Text)
Plants: PlantID (AutoNum, PK), PlantName (Text)
IssueList: IssueID (AutoNum, PK), IssueDescription (Text)
User_Issues: UserID (Num, PK), PlantID (Num, PK), IssueID (Num, PK), HasIssue (Boolean)
The User_Issues table has a composite key made up each identifier from the other tables - this will ensure that a user can't have the same issue for a plant more than once.
When a new user is created a query runs to update the User_Issues table:
INSERT INTO User_Issue(PlantID, IssueID, UserID)
SELECT PlantID, IssueID, UserID
FROM Plants, IssueList, Users
WHERE UserName = "Darren"
This will create a Cartesian product from the plants and issues for each user. So, for example, if you have two plants and three issues you'll get 2x3 records created - a possible 6 issues across the two plants.
This SQL will allow you to allocate an issue:
SELECT UserName
, PlantName
, IssueDesc
, HasIssue
FROM ((
User_Issue INNER JOIN Users ON User_Issue.UserID = Users.UserID)
INNER JOIN Plants ON User_Issue.PlantID = Plants.PlantID)
INNER JOIN IssueList ON User_Issue.IssueID = IssueList.IssueID
ORDER BY PlantName, IssueDesc
To view the issues you just have to add WHERE HasIssue to the above SQL.
SELECT UserName
, PlantName
, IssueDesc
, HasIssue
FROM ((
User_Issue INNER JOIN Users ON User_Issue.UserID = Users.UserID)
INNER JOIN Plants ON User_Issue.PlantID = Plants.PlantID)
INNER JOIN IssueList ON User_Issue.IssueID = IssueList.IssueID
WHERE HasIssue
ORDER BY PlantName, IssueDesc

SQL Querying Help/IF Conditional Statement

So I am creating a booking table. I have two drop down lists called Date and Time and the data for these is being retrieved from a table called "DateTime" with a field called Date with a load of dates in it and same with time.
This booking system is booking time and a date with a Doctor. I'm lost at this point in terms of how do I get times to only appear for a doctor that the user wants to choose an appointment with to avoid double bookings. If a user has booked 9:15am on a certain date, I don't want that time to appear for another user for that date and doctor.
UPDATE Once a user has selected their date time and doctor, this is stored in a table called Booking. So i was originally going to delete that time and date out of that DateTime but this will then not appear for another doctor if selected.
HELP PLEASE. :'(
At least you should share your own table design.
I think there will be two table.
One Master table (booking master table)
--date
--time
--isactive
Another one
doctor booking table
--date
--time
--doctorid
--patientid
--isActive (0,1)-- 0 in cae of cancel
So in GUI I select Doctor and Date,I pass this doctorid and date to my proc
It fetch me date,time etc from proc.which help me to populate calender.
I populate calender for each doctorid and date.
So I have written this on doctor and date change both.
Now while creating Calendar,if I get already book flag then I will show this time,but it will be disable.
My query will be like this
select bm.date,bm.time
,case when db.date=dbm.date then 1 else 0 end isBookedDate
from BookingMaster BM
left join DoctorBookingMapping DBM
on bm.date=dbm.date
where dbm.isactive=1
and db.isactive=1
and dbm.doctorid=#Doctorid
and db.date=#Date
You can do that with a query without deleting data from your DateTime table.
SELECT dt.[Date], dt.[Time], dt.Doctor
FROM dbo.[DateTime] dt
WHERE NOT EXISTS ( SELECT 1
FROM dbo.Booking b
WHERE
b.[Date] = dt.[Date]
AND b.[Time] = dt.[Time]
AND b.Doctor = dt.Doctor)
The query selects all the dates and times that does not currently exist in your booking table.
Hope this helps.
Marius

Most efficient way to get records from a table for which a record exists in another table for each month

I have two tables as below:
User: User_ID, User_name and some other columns (has approx 1000 rows)
Fee: Created_By_User_ID, Created_Date and many other columns (has 17 million records)
Fee table does not have any index (and I can't create one).
I need a list of users for each month of a year (say 2016) who have created at least one fee record.
I do have a working query below which is taking long time to execute. Can someone help me with a better query? May be using EXIST clause (I tried one but still takes time as it scans Fee table)
SELECT MONTH(f.Created_Date), f.Created_By_User_ID
FROM Fees f
JOIN [User] u ON f.Created_By_User_ID= u.User_ID
WHERE f.Created_Date BETWEEN '2016-01-01' AND '2016-12-31'
You will require a full scan of the fee table once in the original query you are using. If you use just the join directly, as you have in the original query, you will require multiple scans of the fee table, many of which will go through redundant rows while the join occurs. Same scenario will occur when you use an inner query as suggested by Mansoor.
An optimization could be to decrease the number of rows on which the joins are happening.
Assuming that the user table contains only one record per user and the Fee table has multiple records per person, we can attempt to find distinct months users made a purchase for by using a CTE.
Then we can make a join on top of this CTE, this will reduce the computation performed by the join and should give a slightly better output time when performing over a large data set.
Try this:
WITH CTE_UserMonthwiseFeeRecords AS
(
SELECT DISTINCT Created_By_User_ID, MONTH(Created_Date) AS FeeMonth
FROM Fee
WHERE Created_Date BETWEEN '2016-01-01' AND '2016-12-31'
)
SELECT User_name, FeeMonth
FROM CTE_UserMonthwiseFeeRecords f
INNER JOIN [User] u ON f.Created_By_User_ID= u.User_ID
Also, you have not mentioned that you require the user names and all, if only id is required for the purpose of finding distinct users making purchases per month, then you can just use the query within the CTE and not even require the JOIN as:
SELECT DISTINCT Created_By_User_ID, MONTH(Created_Date) AS FeeMonth
FROM Fee
WHERE Created_Date BETWEEN '2016-01-01' AND '2016-12-31'
Try below query :
SELECT MONTH(f.Created_Date), f.Created_By_User_ID
FROM Fees f
WHERE EXISTS(SELECT 1 FROM [User] u WHERE f.Created_By_User_ID= u.User_ID
AND DATEDIFF(DAY,f.Created_Date,'2016-01-01') <= 0 AND
DATEDIFF(DAY,f.Created_Date,'2016-12-31') >= 0
You may try this approach to reduce the query run time. however, It does duplicate the huge data and store a instance of table (Temp_Fees), On every DML performed on table Fees/User require truncate and fresh load of table Temp_Fees.
Select * into Temp_Fees from (SELECT MONTH(f.Created_Date) as Created_MONTH, f.Created_By_User_ID
FROM Fees f
WHERE f.Created_Date BETWEEN '2016-01-01' AND '2016-12-31' )
SELECT f.Created_MONTH, f.Created_By_User_ID
FROM Temp_Fees f
JOIN [User] u ON f.Created_By_User_ID= u.User_ID

Add value from another table to my resultset (e.g. find username for user_id)

Some friends dragged me into writing an IRC bot that helps monitoring the consumption of fluids throughout the day. Every user in our channel can submit an amount in liters every time he/she drank something and that value will be stored in a drinks_today table which is reset at the end of the day. The bot uses SQLite for data storage.
I am stuck with an SQL-only way to find out the top 3 drinkers of the day.
I have the following database tables:
CREATE TABLE users(user_id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, active_days INTEGER DEFAULT 0, drinks_total FLOAT DEFAULT 0);
CREATE TABLE drinks_today(user_id INTEGER, amount FLOAT, timestamp INTEGER, FOREIGN KEY(user_id) REFERENCES users(user_id));
I can find the top 3 user_ids as follows:
SELECT user_id,drinks_sum FROM ( SELECT SUM(amount) AS drinks_sum,user_id FROM drinks_today GROUP BY user_id ) ORDER BY drinks_sum DESC LIMIT 3;
The result will be:
1|9.0
4|8.5
3|6.0
Now I am looking for a way to (correctly) map the username into the result set. I tried the following statement, but the result was not correct:
SELECT u.name,drinks_sum FROM ( SELECT SUM(d.amount) AS drinks_sum FROM drinks_today d GROUP BY d.user_id) JOIN users AS u ON u.user_id=user_id ORDER BY drinks_sum DESC LIMIT 3;
The result set will contain the first three users of users table and each will be equipped with the one top score. Which is, of course, completely wrong.
How can I get the username into my result set?
think you can do this all in one.
SELECT u.user_id, u.name, SUM(d.amount) as drunk
FROM users u
INNER JOIN drinks_today dt ON dt.user_id = u.user_id
GROUP BY u.user_id, u.name
ORDER by drunk DESC -- or maybe ORDER BY SUM(d.amount) DESC
LIMIT 3
Edit
Enjoy responsibly.
Cheers.

Complex select query question for hardcore SQL designers

Very complex query been trying to construct it for few days with more real success.
I'm using SQL-SERVER 2005 Standard
What i need is :
5 CampaignVariants from Campaigns whereas 2 are with the largest PPU number set and 3 are random.
Next condition is that CampaignDailyBudget and CampaignTotalBudget are below what is set in Campaign ( calculation is number of clicks in Visitors table connected to Campaigns via CampaignVariants on which users click)
Next condition CampaignLanguage, CampaignCategory, CampaignRegion and CampaignCountry must be the ones i send to this select with (languageID,categoryID,regionID and countryID).
Next condition is that IP address i send to this select statement won't be in IPs list for current Campaign ( i delete inactive for 24 hours IPs ).
In other words it gets 5 CampaignVariants for user that enters the site, when i take from user PublisherRegionUID,IP,Language,Country and Region
view diagram
more details
i get countryID, regionID, ipID, PublisherRegionUID and languageID from Visitor. This are filter parameters. While i first need to get what Publisher is about to show on his site by it's categories, language so on.... and then i filter all remaining Campaigns by Visitors's params with all parameters besides PublisherRegionUID.
So it has two actual fiters. One What Publisher wants to Publish and other one what Visitor can view...
campaignDailyBudget and campaignTotalBudget are values set by Users who creates a Campaign. Those two compared to (number of clicks per campaign)*(campaignPPU) while date filters obviously used to filter for campaignDailyBudget with from 12:00AM to 11:59PM of today. campaignTotalBudget is not filtered by date for obvious reasons
Demo of Stored Procedure
ALTER PROCEDURE dbo.CampaignsGetCampaignVariants4Visitor
#publisherSiteRegionUID uniqueidentifier,
#visitorIP varchar(15),
#browserID tinyint,
#countryID tinyint,
#osID tinyint,
#languageID tinyint,
#acceptsCookies bit
AS
BEGIN
SET NOCOUNT ON;
-- check if such #publisherRegionUID exists
if exists(select publisherSiteRegionID from PublisherSiteRegions where publisherSiteRegionUID=#publisherSiteRegionUID)
begin
declare #publisherSiteRegionID int
select #publisherSiteRegionID = publisherSiteRegionID from PublisherSiteRegions where publisherSiteRegionUID=#publisherSiteRegionUID
-- get CampaignVariants
-- ** choose 2 highest PPU and 3 random CampaignVariants from Campaigns list
-- where regionID,countryID,categoryID,languageID meets Publisher and Visitor requirements
-- and Campaign.campaignDailyBudget<(sum of Clicks in Visitors per this Campaign)*Campaign.PPU during this day
-- and Campaign.campaignTotalBudget<(sum of Clicks in Visitors per this Campaign)*Campaign.PPU
-- and #visitorID does not appear in Campaigns2IPs with this Campaign
-- insert visitor
insert into Visitors (ipAddress,browserID,countryID,languageID,OSID,acceptsCookies)
values (#visitorIP,#browserID,#countryID,#languageID,#OSID,#acceptsCookies)
declare #visitorID int
select #visitorID = IDENT_CURRENT('Visitors')
-- add IP to pool Campaigns ** adding ip to all Campaigns whose CampaignVariants were chosen
-- add PublisherRegion2Visitor relationship
insert into PublisherSiteRegions2Visitors values (#visitorID,#publisherSiteRegionID)
-- add CampaignVariant2Visitor relationship
end
END
GO
I also make a number of assumptions about your oblique requirements. I’ll spell them out as I go along, along with explaining the code. Please note that I of course have no reasonable way of testing this code for typos or minor logic errors.
It might be possible to write this as a single ginormous query, but that would be awkward, ugly, and prone to performance issues as the SQL optimizer can have problems buliding plans for overly-large queries. An option would be to write it as a series of queries, populating temp tables for use in subsequent queries (which alows for much simpler debugging). I chose to write this as a large common table expression statement with a series of CTE tables, largely because it kind of “flows” better that way, and it'd probably perform better than the many-temp-tables version.
First assumption: there are several ciruclar references in there. Campaign has links to both Countries and Regions, so both of these parameter values must be checked—even though based on the table link from Countries to Region, this filter could possibly be simplified to just a check on Country (assuming that the country parameter value is always “in” the region parameter). The same applies to Language and Category, and perhaps to IPs and Visitors. This appears to be sloppy design; if it can be cleared up, or if assumptions on the validity of the data can be made, the query could be simplified.
Second assumption: Parameters are passed in as variables in the form of #Region, #Country, etc. Also, there is only one IP address being passed in; if not, then you’ll need to pass in multiple values, set up a temp table containing those values, and add that as a filter where I use the #IP parameter.
So, step 1 is a first pass identifying “eligible” campaigns, by pulling out all those that share the desired country, region, language, cateogory, and that do not have the one IP address associated with them:
WITH cteEligibleCampaigns (CampaignId)
as (select CampaignId
from Campaigns2Regions
where RegionId = #RegionId
intersect select CampaignId
from Campaign2Countries
where CountryId = #CountryId
intersect select CampaignId
from Campaign2Languages
where LanguageId = #LanguageId
intersect select CampaignId
from Campaign2Categories
where CategoryId = #CategoryId
except select CampaignId
from Campaigns2IPs
where IPID = #IPId)
Next up, from these filter out those items where “CampaignDailyBudget and CampaignTotalBudget are below what is set in Campaign ( calculation is number of clicks in Visitors table connected to Campaigns via CampaignVariants on which users click)”. This requirement is not entirely clear to me. I have chosen to interpret it as “only include those campaigns where, if you count the number of visitors for those campaign’s CampaignVariants, the total count is less than both CampaignDailyBudget and CampaignTotalBudget”. Note that here I introduce a random value, used later on in selecting random rows.
,cteTargetCampaigns (CampaignId, RandomNumber)
as (select CampaignId, checksum(newid() RandomNumber)
from cteEligibleCampaigns ec
inner join Campaigns ca
on ca.CampgainId = ec.CampaignId
inner join CampaignVariants cv
on cv.CampgainId = ec.CampaignId
inner join CampaignVariants2Visitors cvv
on cvv.CampaignVariantId = cv. CampaignVariantId
group by ec.CampaignId
having count(*) < ca.CampaignDailyBudget
and count(*) < CampaignTotalBudget)
Next up, identify the two “best” items.
,cteTopTwo (CampaignId, Ranking)
as (select CampaignId, row_number() over (order by CampgainPPU desc)
from cteTargetCampaigns tc
inner join Campaigns ca
on ca.CampaignId = tc.CampaignId)
Next, line up all other campaigns by the randomly assigned number:
,cteRandom (CampaignId, Ranking)
as (select CampaignId, row_number() over (order by RandomNumber)
from cteTargetCampaigns
where CampaignId not in (select CampaignId
from cteTopTwo
where Ranking < 3))
And, at last, pull the data sets together:
select CampaignId
from cteTopTwo
where Ranking <= 2
union all select CampaignId
from cteRandom
where Ranking <= 3
Lump the above sections of code together, debug typos, invalid assumption, and missed requirements (such as order or flags identifying the top two items from the random ones), and you should be good.
I'm not sure I understand this portion of your post:
it gets 5 CampaignVariants for user
that enters the site, when i take from
user
PublisherRegionUID,IP,Language,Country
and Region
I'm assuming "it" is the query. The user given your second "Next Condition" is the IP? What does "when I take from user" mean? Does that mean that is the information you have at the time you execute your query or is that information you returned from your query? If the later, then there are a host of questions that would need to be answered since many of those columns are part of a Many:Many relationship.
Regardless, below is a means to get the 5 campaigns where, according to your second "Next condition", you have an IP address that you want filter out. I'm also assuming that you want five campaigns total which means that the three random ones cannot include the two "highest PPU" ones.
With
ValidCampaigns As
(
Select C.campaignId
From Campaigns As C
Left Join (Campaigns2IPs As CIP
Join IPs
On IPs.ipID = CIP.ipID
And IPs.ipAddress = #IPAddress)
On CIP.campaignId = C.campaignId
Where CIP.campaignID Is Null
)
CampaignPPURanks As
(
Select C.campaignId
, Row_Number() Over ( Order By C.campaignPPU desc ) As ItemRank
From ValidCampaigns As C
)
, RandomRanks As
(
Select campaignId
, Row_Number() Over ( Order By newid() desc ) As ItemRank
From ValidCampaigns As C
Left Join CampaignPPURanks As CR
On CR.campaignId = C.campaignId
And CR.ItemRank <= 2
Where CR.campaignId Is Null
)
Select ...
From CampaignPPURanks As CPR
Join CampaignVariants As CV
On CV.campaignId = CPR.campaignId
And CPR.ItemRank <= 2
Union All
Select ...
From RandomRanks As RR
Join CampaignVariants As CV
On CV.campaignId = RR.campaignId
And RR.ItemRank <= 3