Calculate days in between certain events based on multiple conditions - sql-server

I am curious if one of you guys can help me to calculate how much days (DAYS BETWEEN TravelStart AND TravelEnd) a certain boat BoatID has been used ONLY for luxury tours BoutTourID = Luxury by different captains CaptainID,
and now for the weird part: UNTIL the next Standard tour BoutTourID = Standard starts. I don't want to take the Cancelled trips into account Status = Cancelled.
CaptainID BoatID BoatTourID Status TravelStart TravelEnd
Jack AlphaBoat Standard 1-7-2019 20-7-2019
Kevin AlphaBoat Luxury 21-7-2019 31-7-2019
Eric AlphaBoat Luxury Cancelled 1-8-2019 10-8-2019
Nick AlphaBoat Standard 11-8-2019 20-8-2019
John AlphaBoat Luxury 21-8-2019 30-8-2019
Lionel BigBoat Standard 1-8-2019 20-8-2019
Jeffrey BigBoat Luxury 20-8-2019 25-8-2019
Chris BigBoat Standard 26-8-2019 28-8-2019
This in SQL should give the following results, so in the basis the table shows the exact same amount of records:
CaptainID
Jack 0 --since BoatTourID = Standard, it should not be calculated
Kevin 10
Eric 0 --since Status = Cancelled
Nick 0
John 9
Lionel 19
Jeffrey 5
Chris 2
It should be possible to run it in 1 SQL query.
The code I wrote so far is very messy and doesn't come close to solving it, so I rather not post it, since I hope for a fresh idea. In case I will still post it, if necessary!

The following query should do what you want:
SELECT
CaptainID
,CASE WHEN BoatTourID = 'Standard' OR [Status] = 'Cancelled' THEN 0
ELSE DATEDIFF(DAY,TravelStart,TravelEnd) AS [Date Difference]
FROM YourTable

You can achieve it by CASE expression along with group by. You can add additional columns in select and group by clause upon your requirement
select CaptinID,
DATEDIFF( DAY,
MIN (CASE WHEN
BoutTourID = 'Luxury' and NOT [Status] = 'Cancelled'
THEN TravelStart Else cast( GETDATE() as date ) ),
MAX (CASE WHEN
BoutTourID = 'Luxury' and NOT [Status] = 'Cancelled'
THEN TravelEnd Else cast ( GETDATE() as date ) )
) Duration_Days
from yourtbale
Group by CaptinID
MIN, MAX and Group by would help you get one line per CaptinID duration from whole trips. In case whole trips per captin not a requirement, you can change remove group by just use CASE expression accordingly..

Related

converting from file path in a column to reference another table with an id number for a filepath

Using Microsoft SQL Server 2012
I've been trying for weeks to get this sorted over my original question for getting a query to work, it does but not correctly. What I want to achieve from this is on the old questions table I'm getting data from has a "filepath" column that the question data refers to from a folder on the local machine this is structured as an example like this: (some columns I haven't included)
Old question table
QuizQuestionID MasterQuestionID MasterQuestionGUID MasterTypeID MasterDifficultyID MasterCategoryID MasterDecadeID QuizQuestionTypeID QuizQuestionDifficultyID QuizQuestionCategoryID QuizQuestionDecadeI D QuestionText AnswerText FilePath IsEditable IsDeletable IsDeleted IsDifficultyOverridden CreatedDate ModifiedDate AUTO_UseCount AUTO_TieBreakerUsageCount AUTO_TieBreakerLastUsed
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1222 1755 0bee472592ce78e7457d87d7a172ff7b 3 2 7 3 3 2 7 3 Name the singer. David Essex /Sounds/CHORUS David Essex - Tahiti.mp3 False True False False 2014-01-18 12:53:59.000 2014-02-07 12:28:55.000 0 NULL NULL
1223 1756 1df7bd191ef5e31b854c7de5f18982d0 1 2 11 NULL 1 2 11 NULL What is this savoury item? Green Chili /Images/General/Greeen Chili.png False True False False 2014-01-18 15:17:39.000 2014-01-26 19:46:00.000 0 NULL NULL
Now in the new question table, a column uses another table called media2 to find the local filepath of files these are structured:
New question table
id uuid type question answer media created_date modified_date created_user modified_user master_category master_decade master_difficulty is_editable is_deletable multiple_choice choice_1 choice_2 choice_3 choice_4 blur_effect related square_1 square_2 square_3 square_4 tie_breaker
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
8033 07B3D24A-6FFA-40AF-B723-B78C9899D4B4 Audio Name the singer. David Essex 21488 2015-03-23 11:51:31.000 2016-03-08 15:21:48.697 NULL NULL 7 2 1 False False False NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
17395 48E555BD-52D6-4358-89E5-8FEE0F0F3AFD Text What is this savoury item? Green chili 19459 2013-09-10 23:51:35.460 2013-09-13 12:51:53.963 NULL NULL 12 NULL 1 False False NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
Media2 table
id UUID name Path Mime/type directory used for Category folder import folder
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
21488 c25183d2-aad7-4c16-8869-9eac711cc39b CHORUS David Essex - Tahiti Quiz C:\media\audio\c25183d2-aad7-4c16-8869-9eac711cc39b.mp3 audio/mp3 Audio Quiz Audio NULL NULL
19459 642db3c3-c531-4818-9b0d-4d7ccd35e0f9 Green chili C:\media\images\642db3c3-c531-4818-9b0d-4d7ccd35e0f9.png image/png Images Quiz Images NULL NULL
Just need to add that "UUID" is a random set of numbers the table uses to identify the file name in their folders so we don't get any duplicated files that may be named the same but don't have the same content and are also used by other software apart from the quiz so I can't use this as a reference to create a query.
This is the whole code that converts the "old questions table" to the "new questions table" the substring is the part that converts the format from old to new which works fine but to clarify it returns null to the "media" column on any image or sound questions, its supposed to convert the filepath column from the old table against the "media2" table and then import all rows to the new question table with the "media" column referencing the "media2" table for filepaths.
--Specify the database, table and columns we want to insert into
INSERT INTO NewDatabase.dbo.quizquestions (uuid, [type], question, answer, created_date, modified_date, master_category, master_decade, master_difficulty, is_editable, is_deletable, media)
--Get the data from the old database (old_database) and map the media filename to the media2 table in the new database
select
questions.uuid,
questions.newtype,
questions.QuestionText,
questions.AnswerText,
questions.CreatedDate,
questions.ModifiedDate,
questions.master_category,
questions.master_decade,
questions.master_difficulty,
questions.IsEditable,
questions.IsDeletable,
media.id as mediaid from
(
select NEWID() as uuid,
CASE
WHEN qqt.TypeName = 'Sound' THEN 'Audio'
ELSE qqt.TypeName
END AS newtype,
qq.QuestionText, qq.AnswerText,
CASE
WHEN qq.createdDate is not NULL THEN qq.createdDate
ELSE GETDATE()
END as createdDate,
CASE
WHEN qq.ModifiedDate is not NULL THEN qq.ModifiedDate
ELSE GETDATE()
END as ModifiedDate,
CASE
WHEN cat.id is NOT NULL THEN cat.id
ELSE 1
END as master_category,
qqdc.QuizQuestionDecadeID as master_decade,
qd.id as master_difficulty,
qq.IsEditable, qq.IsDeletable,
qq.FilePath,
SUBSTRING(
qq.FilePath,
(len(qq.FilePath) - charindex('/', reverse(qq.FilePath)) + 2),
(len(qq.FilePath) - charindex('.', reverse(qq.FilePath))) - (len(qq.FilePath) - charindex('/', reverse(qq.FilePath))) -1 ) as fpath
-- (len(qq.FilePath) - charindex('.', reverse(qq.FilePath)) as positionoflastdot),
--(len(qq.FilePath) - charindex('/', reverse(qq.FilePath)) as positionoflastslash),
from Olddatabase.dbo.QuizQuestion qq
left join Olddatabase.dbo.QuizQuestionType qqt on qq.QuizQuestionTypeID = qqt.QuizQuestionTypeID
left join Olddatabase.dbo.QuizQuestionDifficulty qqd on qq.QuizQuestionDifficultyID = qqd.QuizQuestionDifficultyID
left join Olddatabase.dbo.QuizQuestionCategory qqc on qq.QuizQuestionCategoryID = qqc.QuizQuestionCategoryID
left join Olddatabase.dbo.QuizQuestionDecade qqdc on qq.QuizQuestionDecadeID = qqdc.QuizQuestionDecadeID
left join Olddatabase.dbo.QuizQuestionCategory qqmc on qq.MasterCategoryID = qqmc.MasterQuestionCategoryID
left join Newdatabase.dbo.QuizCategories cat on qqc.CategoryName = cat.name
left join Newdatabase.dbo.QuizDifficulties qd on qd.id = qq.MasterDifficultyID
where qq.MasterCategoryID is not null
) as questions
left join Newdatabase.dbo.Media2 media on media.name = replace(fpath, 'quiz','')
i cant really change to structure of the tables as the software relies heavily on it, and changing it manually would be painful and time consuming as there are over 2000 audio/images questions
i'm just getting my head around SQL databases and queries but this has stopped me dead from progressing.
any help changing this above query or creating something entirely different would be greatly appreciated.
Just looking at it, the code seems properly structured. Faced with something like this, I’d “cut down” the query to only the relevant parts, and debug from there, tossing in extra lines like those you currently have commented out to review the parts of the function calls being returned. Pick it part, look at all th details, and you’ll eventually figure out what’s not lining up. The following should work as an initial "minimum query":
select
qquestions.FilePath
,questions.fpath
,media.id as mediaid
from (-- Build the necessary filepath. Running just the subquery can help figure things out too
select
qq.FilePath
,SUBSTRING(
qq.FilePath
,(len(qq.FilePath) - charindex('/', reverse(qq.FilePath)) + 2)
,(len(qq.FilePath) - charindex('.', reverse(qq.FilePath))) - (len(qq.FilePath) - charindex('/', reverse(qq.FilePath))) -1 ) as fpath
from Olddatabase.dbo.QuizQuestion qq
where qq.MasterCategoryID is not null
) as questions
left join Newdatabase.dbo.Media2 media
on media.name = replace(fpath, 'quiz','')
Mess with this and it should be easier to figre things out.
Bonus advice: code like
CASE
WHEN qq.createdDate is not NULL THEN qq.createdDate
ELSE GETDATE()
END as createdDate,
can be replaced with the shorter and more legible form
isnull(qq.createdDate, getdate()) createdDate,

SQL Server, Having Clause, Where, Aggregate Functions

In my problem which I am trying to solve, there is a performance values table:
Staff PerformanceID Date Percentage
--------------------------------------------------
StaffName1 1 2/15/2016 95
StaffName1 2 2/15/2016 95
StaffName1 1 2/22/2016 100
...
StaffName2 1 2/15/2016 100
StaffName2 2 2/15/2016 100
StaffName2 1 2/22/2016 100
And the SQL statement as follows:
SELECT TOP (10)
tbl_Staff.StaffName,
ROUND(AVG(tbl_StaffPerformancesValues.Percentage), 0) AS AverageRating
FROM
tbl_Staff
INNER JOIN
tbl_AcademicTermsStaff ON tbl_Staff.StaffID = tbl_AcademicTermsStaff.StaffID
INNER JOIN
tbl_StaffPerformancesValues ON tbl_AcademicTermsStaff.StaffID = tbl_StaffPerformancesValues.StaffID
WHERE
(tbl_StaffPerformancesValues.Date >= #DateFrom)
AND (tbl_AcademicTermsStaff.SchoolCode = #SchoolCode)
AND (tbl_AcademicTermsStaff.AcademicTermID = #AcademicTermID)
GROUP BY
tbl_Staff.StaffName
ORDER BY
AverageRating DESC, tbl_Staff.StaffName
What I am trying to do is, from a given date, for instance 02-22-2016,
I want to calculate average performance for each staff member.
The code above gives me average without considering the date filter.
Thank you.
Apart from your join conditions and table names which looks quite complex, One simple question, If you want the results for a particular date then why is the need of having
WHERE tbl_StaffPerformancesValues.Date >= #DateFrom
As you said your query is displaying average results but not for a date instance. Change the above line to WHERE tbl_StaffPerformancesValues.Date = #DateFrom.
Correct me if I am wrong.
Thanks for the replies, the code above, as you all say and as it is also expected is correct.
I intended to have a date filter to see the results from the given date until now.
The code
WHERE tbl_StaffPerformancesValues.Date >= #DateFrom
is correct.
The mistake i found from my coding is, in another block i had the following:
Protected Sub TextBoxDateFrom_Text(sender As Object, e As System.EventArgs) Handles TextBoxDate.PreRender, TextBoxDate.TextChanged
Try
Dim strDate As String = Date.Parse(DatesOfWeekISO8601(2016, WeekOfYearISO8601(Date.Today))).AddDays(-7).ToString("dd/MM/yyyy")
If Not IsPostBack Then
TextBoxDate.Text = strDate
End If
SqlDataSourcePerformances.SelectParameters("DateFrom").DefaultValue = Date.Parse(TextBoxDate.Text, CultureInfo.CreateSpecificCulture("id-ID")).AddDays(-7)
GridViewPerformances.DataBind()
Catch ex As Exception
End Try
End Sub
I, unintentionally, applied .AddDays(-7) twice.
I just noticed it and removed the second .AddDays(-7) from my code.
SqlDataSourcePerformances.SelectParameters("DateFrom").DefaultValue = Date.Parse(TextBoxDate.Text, CultureInfo.CreateSpecificCulture("id-ID"))
Because of that mistake, the SQL code was getting the performance values 14 days before until now. So the average was wrong.
Thanks again.

Update query failed '0' rows affected

I am trying to run this query but the query keeps giving up on me:
Update StockInvoiceInfo set Quantity = Quantity - 2 where p_id = 5 AND ProductDate = convert(Cast('31-5-2015' as datetime)) ;
After Running this code it returns an error below:
Incorrect syntax near '31-5-2015'
The datatype of the ProductDate column isDate. I am using Sql Server 2012.
You have used Convert functions but didn't supplied it with parameters. Also there is no need for this function here. Also take care of date format. I have changed it to standard format:
Update StockInvoiceInfo set Quantity = Quantity - 2
where p_id = 5 AND ProductDate = Cast('2015-05-31' as datetime)
If all you are trying to do is compare a Sql Date, then just use an agnostic format like '20150531' or easier to read '2015-05-31'. No need for casts or convert at all, i.e.
WHERE ... AND ProductDate = '2015-05-31'
However, if ProductDate isn't a date, but one of the *DATETIME* data types, and you are looking to update any time on the same day, then I believe you are looking for something like:
Update StockInvoiceInfo
set Quantity = Quantity - 2
where
p_id = 5
AND CAST(ProductDate AS DATE) = '2015-05-31';
However, the performance will be lousy as the clause isn't likely to be SARGable. You're better off simply doing:
AND ProductDate >= '2015-05-31' AND ProductDate < '2015-06-01';
(Resist the temptation to use between, or hacks like ':23:59:59' as there will be data corner cases which will bite you)
use CAST('5-31-2015' as DATETIME)
with the above update statement you started convert but with incomplete syntax
here the convert syntax
Update StockInvoiceInfo
set Quantity = Quantity - 2
where p_id = 5
AND ProductDate = convert(datetime,Cast('31-5-2015' as varchar),103) ;

How can I write a better stored procedure to return all records with a flag/bit field = 1 in which I pass the flag-field's name into the sproc?

I'm dealing with a substantially more complicated schema (of course), but I think i'm including just enough in this question to show what I need and no more. (I'll update as needed based on comments.) Let's say I have this table:
create table peeps (
id int primary key identity(1,1),
name varchar(50),
eligible bit,
lefty bit,
contractor bit
)
And I want to write a sproc that's going to return the names of all my peeps if they are eligible or lefties or contractors and I want to have a single procedure to handle all those cases.
My current approach to this (which seems to totally work) looks like this:
CREATE PROCEDURE getFlagMatchingPeeps #flagName varchar(30)
select name
from peeps
where
(eligible = 1 or eligible = case when #flagName = 'eligible' then 1 else 0 end) and
(lefty = 1 or lefty = case when #flagName = 'lefty' then 1 else 0 end) and
(contractor = 1 or contractor = case when #flagName = 'contractor' then 1 else 0 end)
But that feels like an ugly solution (e.g. testing whether a value is 1 twice feels wasteful). So I'm here looking for more advanced SQL people to help me craft something better.
I guess first, is there anything wrong with my solution? If not, this can be quick. But if there is:
What is it?
What's the right approach?
How should I have identified the problem?
How should I be thinking differently in order to solve this?
If you just wanted to get rid of the extra comparison (for simplicity, this assumes that the bit columns are non-nullable contrary to the defined schema)
SELECT
[name]
FROM
[dbo].[peeps]
WHERE
([eligible] = CASE WHEN #flagName = 'eligible' THEN 1 ELSE [eligible] END) AND
([lefty] = CASE WHEN #flagName = 'lefty' THEN 1 ELSE [lefty] END) AND
([contractor] = CASE WHEN #flagName = 'contractor' THEN 1 ELSE [contractor] END);
Ignoring the fact that you are filtering on bit columns, in general there will be performance issues with queries using dynamic search parameters without the use of OPTION(RECOMPILE) or dynamic sql due to parameter sniffing. See Erland Sommarskog's article on this topic.

Dynamic column value with group by (sql server and Linq)

i have a table Plan with following sample data
i want to aggregate the result by PlanMonth and for PlanStatus i want that if any of its (in a group) values is Drafted i get drafted in the result and Under Approval otherwise. i have done it using following query
select PlanMonth, case when Flag=1 then 'Drafted' else 'Under Approval' end as PlanStatus
from
(select p.PlanMonth, Max(CASE WHEN p.PlanStatus = 'Drafted' THEN 1 ELSE 0 END) Flag
from Plans p
group by p.PlanMonth
) inquery
i have consulted this blog post. Is there something wrong with it? Moreover, if someone can help me translate it to linq i will be grateful
The query you have will work.
With the sample data you have provided it can be simplified a bit.
select p.PlanMonth,
min(p.PlanStatus) as PlanStatus
from Plans as p
group by p.PlanMonth
This will not work if you have values in PlanStatus that is alphabetically sorted before Drafted.
Following Linq query worked for me
return (from p in db.mktDrVisitPlans
group p by p.PlanMonth into grop
select
new VMVisitPlan
{
PlanMonth = grop.Key
PlanStatus = grop.Any(x=>x.p.PlanStatus == "Drafted")?"Hold":"Under Approval",
}).ToList();

Resources