Getting current semester in SQL SERVER - sql-server

I'm having a trouble on this query it says Incorrect syntax near the keyword 'BETWEEN' but when I tried to individually execute the BETWEEN conditions without CASE WHEN it runs normally, Also in relation to the Title is their a proper way to get the semester in SQL SERVER?
DECLARE #ActiveSemester INT
SET #ActiveSemester = ISNULL((SELECT [value]
FROM spms_tblProfileDefaults WHERE eid = 7078 and ps_id = 1), (SELECT [value]
FROM spms_configs WHERE actions = 'semester_active') )
SELECT FORMAT((SUM((DATEDIFF(MINUTE, a.actual_start_time, a.actual_end_time)
- isnull(datediff(minute, break_start, break_end), 0))/ 60.0)), '0.00')
FROM spms_tblSubTask as a
LEFT JOIN pmis.dbo.employee as b ON a.eid = b.eid
LEFT JOIN pmis.dbo.time_reference as c ON c.code = ISNULL(b.TimeReference, 'TIME14')
LEFT JOIN dbo.spms_vwOrganizationalChart as e ON a.eid = e.eid
cross apply
(
select break_start = case when c.break_from between a.actual_start_time and a.actual_end_time
then c.break_from
when a.actual_start_time between c.break_from and c.break_to
then a.actual_start_time
else NULL
end,
break_end = case when c.break_to between a.actual_start_time and a.actual_end_time
then c.break_to
when a.actual_end_time between c.break_from and c.break_to
then a.actual_end_time
end
) as d
WHERE
b.Shift = 0 and a.eid = 7078 and
YEAR(a.start_date_time) = YEAR(GETDATE()) and a.action_code = 1
and
(CASE
WHEN
#ActiveSemester = 1
THEN
a.start_date_time BETWEEN CAST(CONCAT('01/01/',YEAR(GETDATE())) as date) AND
CAST(CONCAT('06/30/',YEAR(GETDATE())) as date)
ELSE
a.start_date_time BETWEEN CAST(CONCAT('07/01/',YEAR(GETDATE())) as date) AND
CAST(CONCAT('12/31/',YEAR(GETDATE())) as date)
END)

It seems that the other answer was Deleted and here's what I did as the other points out about the proper use of CASE WHEN
WHERE
'other condition' and
a.start_date_time BETWEEN
(CASE
WHEN #ActiveSemester = '1'
THEN
CAST(CONCAT('01/01/',YEAR(GETDATE())) as date)
ELSE
CAST(CONCAT('07/01/',YEAR(GETDATE())) as date)
END)
AND
(CASE
WHEN #ActiveSemester = '1'
THEN
CAST(CONCAT('06/30/',YEAR(GETDATE())) as date)
ELSE
CAST(CONCAT('12/31/',YEAR(GETDATE())) as date)
END)
with this, I get the results I wanted. Thanks

Assuming #ActiveSemester can take on the values 0 or 1, I'd just write this as:
WHERE
b.Shift = 0 and a.eid = 7078 and
YEAR(a.start_date_time) = YEAR(GETDATE()) and a.action_code = 1
and
#ActiveSemester = ((MONTH(a.start_date_time)-1)/6)
Now, it's worth noting that this expression cannot use an index on start_date_time to satisfy this final predicate. If that's an issue, and the query isn't performing well, then I'd either recommend persisted computed columns in this table to extract the year and month value from the start_date_time column, modify the above query to use those columns and add appropriate indexes, or to populate a calendar table which has such values (and semester) properly represented and join to that table above, to again simplify the expressions and allow indexes to be used.
Note, also, that the column name worries me. If it does contain a time portion as well as a date, then note that the expressions in your original query would have excluded any values that occurred after midnight on 30th June and 31st December1. I presume that wasn't your intent and the query above doesn't suffer this same shortcoming.
1Because 2018-06-30T00:01:00 is strictly later than 2018-06-30T00:00:00 (which is what CAST(CONCAT('06/30/',YEAR(GETDATE())) as date) expands out to) and so isn't BETWEEN the start and end dates you gave.

Related

Update row value when record not found on other database

I'm very new to SQL. I need help in how to update "AH_Field" in database sx_appmts.db, table "Sx_Appmts_table" with a value of "No Show" when I do not find a record in the sx_services_db, table sx_services_table.
This code works fine when it finds a match and puts the value "Done" in the AH_Field, however I wanted to place the value of "No Show" when no match is found.
UPDATE S
SET [AH_Field] = 'Done'
FROM [SX_Appmts_Db].[dbo].[Sx_Appmts_Table] S
INNER JOIN [SX_Services_db].[dbo].[Sx_Services_Table] D ON S.[AA_field] = D.[WK_Field]
WHERE S.[AH_Field] LIKE 'Scheduled' AND
S.[AA_Field] = D.[WK_Field] AND
S.[AK_Field] IS NOT NULL AND
S.[AQ_Field] IS NOT NULL AND
CONVERT(VARCHAR(10), S.[AK_Field], 101) = CONVERT(VARCHAR(10), D.[DV_Field], 101) AND
CONVERT(VARCHAR(10), S.[AQ_Field], 101) = CONVERT(VARCHAR(10), D.[AD_Field], 101) AND
S.[AD_Field] LIKE D.[AA_Field]
Use a LEFT JOIN instead of an INNER JOIN because you want the unmatched rows too and a CASE expression or IIF() to update the column:
UPDATE S
SET [AH_Field] = IIF(D.[WK_Field] IS NULL, 'No Show', 'Done')
FROM [SX_Appmts_Db].[dbo].[Sx_Appmts_Table] S LEFT JOIN [SX_Services_db].[dbo].[Sx_Services_Table] D
ON S. [AA_field] = D.[WK_Field]
AND S.[AA_Field] = D.[WK_Field]
AND S.[AD_Field] LIKE D.[AA_Field]
AND convert(varchar(10), S.[AK_Field], 101) = convert(varchar(10), D.[DV_Field], 101)
AND convert(varchar(10), S.[AQ_Field], 101) = convert(varchar(10), D.[AD_Field], 101)
WHERE S.[AH_Field] LIKE 'Scheduled' AND S.[AK_Field] IS NOT NULL AND S.[AQ_Field] IS NOT NULL
The conditions that you had in the WHERE clause involving columns from the table [Sx_Services_Table] are moved to the ON clause to preserve the integrity of the LEFT JOIN.
Also I don't see the reason for the operator LIKE in this condition:
S.[AH_Field] LIKE 'Scheduled'
You can use = instead of LIKE.
The important point is LEFT JOIN, but I would make a few changes to the query:
UPDATE S
SET AH_Field = (CASE WHEN ST.WK_Field IS NOT NULL THEN 'Done' ELSE 'No Show' END)
FROM [SX_Appmts_Db].[dbo].[Sx_Appmts_Table] S LEFT JOIN
[SX_Services_db].[dbo].[Sx_Services_Table] ST
ON S.[AA_field] = ST.[WK_Field] AND
CONVERT(DATE, S.AK_Field) = CONVERT(DATE, ST.DV_Field) AND
CONVERT(DATE, S.AQ_Field) = CONVERT(DATE, ST.AD_Field) AND
S.AD_Field LIKE ST.AA_Field
WHERE S.[AH_Field] = 'Scheduled';
Notes:
There is no need to put columns names in square braces unless they need to be quotes (and having to quote identifiers means you have a bad choice of identifier).
D --> ST, an abbreviation for the table name.
There is no need to repeat the S.[AA_Field] = D.[WK_Field] comparison.
LIKE 'Scheduled' is pretty much equivalent to = 'Scheduled', but the latter is clearer in intent.
Date comparisons are best made using date functions, not strings.
The comparisons to NULL are superfluous. The equality comparisons already filter out NULL values.

SQL - Finding Gaps in Coverage

I am running this problem on SQL server
Here is my problem.
have something like this
Dataset A
FK_ID StartDate EndDate Type
1 10/1/2018 11/30/2018 M
1 12/1/2018 2/28/2019 N
1 3/1/2019 10/31/2019 M
I have a second data source I have no control over with data something like this:
Dataset B
FK_ID SpanStart SpanEnd Type
1 10/1/2018 10/15/2018 M
1 10/1/2018 10/25/2018 M
1 2/15/2019 4/30/2019 M
1 5/1/2019 10/31/2019 M
What I am trying to accomplish is to check to make sure every date within each TYPE M record in Dataset A has at least 1 record in Dataset B.
For example record 1 in Dataset A does NOT have coverage from 10/26/2018 through 11/30/2018. I really only care about when the coverage ends, in this case I want to return 10/26/2018 because it is the first date where the span has no coverage from Dataset B.
I've written a function that does this but it is pretty slow because it is cycling through each date within each M record and counting the number of records in Dataset B. It exits the loop when it finds the first one but I would really like to make this more efficient. I am sure I am not thinking about this properly so any suggestions anyone can offer would be helpful.
This is the section of code I'm currently running
else if #SpanType = 'M'
begin
set #CurrDate = #SpanStart
set #UncovDays = 0
while #CurrDate <= #SpanEnd
Begin
if (SELECT count(*)
FROM eligiblecoverage ec join eligibilityplan ep on ec.plandescription = ep.planname
WHERE ec.masterindividualid = #IndID
and ec.planbegindate <= #CurrDate and ec.planenddate >= #CurrDate
and ec.sourcecreateddate = #MaxDate
and ep.medicaidcoverage = 1) = 0
begin
SET #Result = concat('NON Starting ',format(#currdate, 'M/d/yyyy'))
BREAK
end
set #CurrDate = #CurrDate + 1
end
end
I am not married to having a function it just could not find a way to do this in queries that wasn't very very slow.
EDIT: Dataset B will never have any TYPEs except M so that is not a consideration
EDIT 2: The code offered by DonPablo does de-overlap the data but only in cases where there is an overlap at all. It reduces dataset B to:
FK_ID SpanStart SpanEnd Type
1 10/1/2018 10/25/2018 M
instead of
FK_ID SpanStart SpanEnd Type
1 10/1/2018 10/25/2018 M
1 2/15/2019 4/30/2019 M
1 5/1/2019 10/31/2019 M
I am still futzing around with it but it's a start.
I would approach this by focusing on B. My assumption is that any absent record would follow span_end in the table. So here is the idea:
Unpivot the dates in B (adding "1" to the end dates)
Add a flag if they are present with type "M".
Check to see if any not-present records are in the span for A.
Check the first and last dates as well.
So, this looks like:
with bdates as (
select v.dte,
(case when exists (select 1
from b b2
where v.dte between b2.spanstart and b2.spanend and
b2.type = 'M'
)
then 1 else 0
end) as in_b
from b cross apply
(values (spanstart), (dateadd(day, 1, spanend)
) v(dte)
where b.type = 'M' -- all we care about
group by v.dte -- no need for duplicates
)
select a.*,
(case when not exists (select 1
from b b2
where a.startdate between b2.spanstart and b2.spanend and
b2.type = 'M'
)
then 0
when not exists (select 1
from b b2
where a.enddate between b2.spanstart and b2.spanend and
b2.type = 'M'
)
when exists (select 1
from bdates bd
where bd.dte between a.startdate and a.enddate and
bd.in_b = 0
)
then 0
when exists (select 1
from b b2
where a.startdate between b2.spanstart and b2.spanend and
b2.type = 'M'
)
then 1
else 0
end)
from a;
What is this doing? Four validity checks:
Is the starttime valid?
Is the endtime valid?
Are any intermediate dates invalid?
Is there at least one valid record?
Start by framing the problem in smaller pieces, in a sequence of actions like I did in the comment.
See George Polya "How To Solve It" 1945
Then Google is your friend -- look at==> sql de-overlap date ranges into one record (over a million results)
UPDATED--I picked Merge overlapping dates in SQL Server
and updated it for our table and column names.
Also look at theory from 1983 Allen's Interval Algebra https://www.ics.uci.edu/~alspaugh/cls/shr/allen.html
Or from 2014 https://stewashton.wordpress.com/2014/03/11/sql-for-date-ranges-gaps-and-overlaps/
This is a primer on how to setup test data for this problem.
Finally determine what counts via Ranking the various pairs of A vs B --
bypass those totally Within, then work with earliest PartialOverlaps, lastly do the Precede/Follow items.
--from Merge overlapping dates in SQL Server
with SpanStarts as
(
select distinct FK_ID, SpanStart
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanStart < t1.SpanStart
and t2.SpanEnd >= t1.SpanStart)
),
SpanEnds as
(
select distinct FK_ID, SpanEnd
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanEnd > t1.SpanEnd
and t2.SpanStart <= t1.SpanEnd)
),
DeOverlapped_B as
(
Select FK_ID, SpanStart,
(select min(SpanEnd) from SpanEnds as e
where e.FK_ID = s.FK_ID
and SpanEnd >= SpanStart) as SpanEnd
from SpanStarts as s
)
Select * from DeOverlapped_B
Now we have something to feed into the next steps, and we can use the above as a CTE
======================================
with SpanStarts as
(
select distinct FK_ID, SpanStart
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanStart < t1.SpanStart
and t2.SpanEnd >= t1.SpanStart)
),
SpanEnds as
(
select distinct FK_ID, SpanEnd
from Coverage_B as t1
where not exists
(select * from Coverage_B as t2
where t2.FK_ID = t1.FK_ID
and t2.SpanEnd > t1.SpanEnd
and t2.SpanStart <= t1.SpanEnd)
),
DeOverlapped_B as
(
Select FK_ID, SpanStart,
(select min(SpanEnd) from SpanEnds as e
where e.FK_ID = s.FK_ID
and SpanEnd >= SpanStart) as SpanEnd
from SpanStarts as s
),
-- find A row's coverage
ACoverage as (
Select
a.*, b.SpanEnd, b.SpanStart,
Case
When SpanStart <= StartDate And StartDate <= SpanEnd
And SpanStart <= EndDate And EndDate <= SpanEnd
Then '1within' -- starts, equals, during, finishes
When EndDate < SpanStart
Or SpanEnd < StartDate
Then '3beforeAfter' -- preceeds, meets, preceeded, met
Else '2overlap' -- one or two ends hang over spanStart/End
End as relation
From Coverage_A a
Left Join DeOverlapped_B b
On a.FK_ID = b.FK_ID
Where a.Type = 'M'
)
Select
*
,Case
When relation1 = '2' And StartDate < SpanStart Then StartDate
When relation1 = '2' Then DateAdd(d, 1, SpanEnd)
When relation1 = '3' Then StartDate
End as UnCoveredBeginning
From (
Select
*
,SUBSTRING(relation,1,1) as relation1
,ROW_NUMBER() Over (Partition by A_ID Order by relation, SpanStart) as Rownum
from ACoverage
) aRNO
Where Rownum = 1
And relation1 <> '1'

SQL SUM() on one field with other fields on other conditions (the most efficient way)

I have a stored procedure to retrieve total orders (in $$$) on particular date and at the same time need to find out how much was a cash transaction vs credit card transaction. There are also others transaction type (eg. paypal) but does not need to be returned (don't ask me why).
I have two choices, writing all in 1 query or go through cursor. Here is my 1 query
SELECT #TotalOrder = SUM([TotalValue]),
#TotalCash = (SELECT SUM([TotalValue]) FROM [dbo].[tblOrder] dbChild WHERE (dbChild.[OrderId] = dbMain.[OrderId]) AND (dbChild.[PaymentType] = 0)),
#TotalCard = (SELECT SUM([TotalValue]) FROM [dbo].[tblOrder] dbChild WHERE (dbChild.[OrderId] = dbMain.[OrderId]) AND (dbChild.[PaymentType] = 1))
FROM [dbo].[tblOrder] dbMain
WHERE [PaymentDate] BETWEEN #StartDate AND #EndDate;
Is the query above ok? Alternatively, I can use cursor which loop through each record and accumulate the #TotalOrder, #TotalCash, and #TotalCard.
SELECT [TotalValue], [PaymentType]
FROM [dbo].[tblOrder] dbMain
WHERE [PaymentDate] BETWEEN #StartDate AND #EndDate;
--then use cursor to loop through each of the record.
What do you think? Which one is the most efficient way? If both is not efficient, do you have any other suggestions?
The first method will most likely be the fastest, however you can refactor to use a single query:
SELECT #TotalOrder = SUM([TotalValue]),
#TotalCash = SUM(CASE WHEN [PaymentType] = 0 THEN [TotalValue] ELSE 0 END)
#TotalCard = SUM(CASE WHEN [PaymentType] = 1 THEN [TotalValue] ELSE 0 END)
FROM [dbo].[tblOrder] dbMain
WHERE [PaymentDate] BETWEEN #StartDate AND #EndDate;

Select from same column under different conditons

I need to join these two tables. I need to select occurrences where:
ex_head of_family_active = 1 AND tax_year = 2017
and also:
ex_head of_family_active = 0 AND tax_year = 2016
The first time I tried to join these two tables I got the warehouse data
dbo.tb_master_ascend AND warehouse_data.dbo.tb_master_ascend in the from clause have the same exposed names. As the query now shown below, I get a syntax error on the "where". What am I doing wrong? Thank you
use [warehouse_data]
select
parcel_number as Account,
pact_code as type,
owner_name as Owner,
case
when ex_head_of_family_active >= 1
then 'X'
else ''
end 'Head_Of_Fam'
from
warehouse_data.dbo.tb_master_ascend
inner join
warehouse_data.dbo.tb_master_ascend on parcel_number = parcel_number
where
warehouse_data.dbo.tb_master_ascend.tax_year = '2016'
and ex_head_of_family_active = 0
where
warehouse_data.dbo.tb_master_ascend.t2.tax_year = '2017'
and ex_head_of_family_active >= 1
and (eff_from_date <= getdate())
and (eff_to_date is null or eff_to_date >= getdate())
#marc_s I changed the where statements and updated my code however the filter is not working now:
use [warehouse_data]
select
wh2.parcel_number as Account
,wh2.pact_code as Class_Type
,wh2.owner_name as Owner_Name
,case when wh2.ex_head_of_family_active >= 1 then 'X'
else ''
end 'Head_Of_Fam_2017'
from warehouse_data.dbo.tb_master_ascend as WH2
left join warehouse_data.dbo.tb_master_ascend as WH1 on ((WH2.parcel_number = wh1.parcel_number)
and (WH1.tax_year = '2016')
and (WH1.ex_head_of_family_active is null))
where WH2.tax_year = '2017'
and wh2.ex_head_of_family_active >= 1
and (wh2.eff_from_date <= getdate())
and (wh2.eff_to_date is null or wh2.eff_to_date >= getdate())
I would use a CTE to get all your parcels that meet your 2016 rules.
Then join that against your 2017 rules on parcel ID.
I'm summarizing:
with cte as
(
select parcelID
from
where [2016 rules]
group by parcelID --If this isn't unique you will cartisian your results
)
select columns
from table
join cte on table.parcelid=cte.parcelID
where [2017 rules]

TSQL Subquery effeciency

I am trying to write a subquery in a view to be returned as a column but I am not exactly sure what would give me the most efficient call.
I have View A that gathers a bunch of fields from different tables (one table being Listings that has has a 1 to many relationship with OpenHours) then one of the fields I want it to be from another table (OpenHours) that will only be Today's Open hours field.
The OpenHours table has ListingID, Day (0 based for the day of the week), Hours (text of the open hours such as "8:00am-5:00pm"). Here is what I need to do:
Check if OpenTable has a record for that particular listing that day = 7, if its 7 (which is not a day of the week) then return "Open 24 hours".
If does not exist then return the next record, since SQL Servers datepart(dw.. is 1 based, following will be used select datepart(dw,getdate())-1 to get a 0 day based day of week starting on Sunday (Sunday being 0)
Return nothing if no records exist that match the criteria.
I would appreciate some help on this. I attempted to write this but could not get far. I am not sure how to declare variables for day of the week in the view.
UPDATE
here is my function, anyone see any glaring inefficiencies?
ALTER FUNCTION [dbo].[GetTodaysOpenHours](#ListingID int)
RETURNS VARCHAR(50)
AS
BEGIN
DECLARE #DayOfWeek int
--SQL Day of week starts on sunday but it is 1 based, listing open hours are 0 based
SET #DayOfWeek = DATEPART(DW, GETDATE()) - 1
DECLARE #OpenHours VARCHAR(50)
IF EXISTS(SELECT * FROM OpenHours WHERE Day = 7 AND ListingID = #ListingID)
SET #OpenHours = 'Open 24 Hours'
ELSE
SELECT #OpenHours = Hours FROM OpenHours WHERE ListingID = #ListingID AND Day = #DayOfWeek
RETURN #OpenHours
END
UPDATED VIEW
ALTER view [dbo].[vListings]
as
SELECT l.ListingID, l.ExpiryDate, l.IsApproved, l.IsActive, l.Position,MoneyField1, DateField1,
IntField1, IntField2, IntField3, IntField4,
BoolField1, BoolField2, BoolField3,
OptionField1, OptionField2, OptionField3, OptionField4,
IsTop, TopStartDate, TopExpireDate, Address, Address + ' ' + c.Name + ' ' + p.Name AS FullAddress,
o1.Description as Options1Description,
o2.Description as Options2Description,
o3.Description as Options3Description,
o4.Description as Options4Description,
COALESCE(
(SELECT TOP 1 ThumbnailPath
FROM Attachments
WHERE ListingID = l.listingID), '/content/images/noImageThumbnail2.jpg') AS MainThumbnail,
COALESCE(
(SELECT TOP 1 ThumbnailPath2
FROM Attachments
WHERE ListingID = l.listingID), '/content/images/noImageThumbnail.jpg') AS MainThumbnail2,
l.UserID,
c.SubDomainName as CitySubDomainName, l.Name,
CASE
WHEN l.IsAutoGenerated = 1 THEN l.ImportedPhoneNumber
ELSE dbo.FormatPhoneNumber(u.PhoneNumber)
END as PhoneNumber,
CASE
WHEN l.IsAutoGenerated = 1 THEN l.ImportedContactInfo
ELSE u.FirstName + ' ' + u.LastName
END as ContractInfo,
p.Abbv as StateAbbv,
cn.Code as CountryCode,
l.Comments, l.UniqueID, l.Rating, l.Website,
(select L.ListingID,
isnull(H1.Hours, H2.Hours) as Hours
from Listings L
outer apply (
select Hours FROM OpenHours H WHERE H.Day = 7
AND H.ListingID = L.ListingID
) H1
outer apply (
select Hours FROM OpenHours H WHERE Day = DATEPART(DW, GETDATE()) - 1
AND H.ListingID = L.ListingID
) H2
--dbo.GetTodaysOpenHours(l.ListingID) as TodaysOpenHours
FROM Listings l
INNER JOIN Cities c ON c.CityID = l.CITYID
INNER JOIN Provinces p ON p.ProvinceID = c.ProvinceID
INNER JOIN Countries cn ON cn.CountryID = p.CountryID
INNER JOIN AspNetUsers u ON u.Id = l.UserID
LEFT OUTER JOIN Options1 o1 ON o1.OptionID = l.OptionField1
LEFT OUTER JOIN Options2 o2 ON o2.OptionID = l.OptionField2
LEFT OUTER JOIN Options3 o3 ON o3.OptionID = l.OptionField3
LEFT OUTER JOIN Options4 o4 ON o4.OptionID = l.OptionField4
GO
I get an error that says "incorrect syntax near the keyword FROM
(FROM Listings l)
I upadted the view (added FROM) to the select statements as well
I prefer not to use t he function because I had to add an index to my openhours table on listingid and day in order to make it a little faster but if adding sql into view itself would be better that would be awesome
User defined function isn't usually the best performing solution. You could try just to add that SQL into the view / query by doing something like this (sorry, can't test this, hopefully there's no syntax errors):
select
L.ListingID,
isnull(H1.Hours, H2.Hours) as Hours
from Listing L
outer apply (
select Hours OpenHours H WHERE H.Day = 7
AND H.ListingID = L.ListingID
) H1
outer apply (
select Hours OpenHours H WHERE Day = DATEPART(DW, GETDATE()) - 1
AND H.ListingID = L.ListingID
) H2

Resources