Using OVER() if customer has watch gladiator then 1 else 0 SQL SERVER - sql-server

I think I need some guidance as to what is wrong in my query. I am trying to do
Watched_Gladiator=CASE WHEN FilmName IN (CASE WHEN FilmName LIKE '%Gladiator%' THEN 1 END) then OVER(PARTITION BY Cust_Nr) THEN 1 ELSE 0 END
Tried this one too:
Watched_Gladiator=CASE WHEN FilmName IN (CASE WHEN FilmName LIKE '%Gladiator%' THEN Filmnamne END) then OVER(PARTITION BY Cust_Nr) THEN 1 ELSE 0 END
The Error I am currently getting is this:
Incorrect syntax near the keyword 'OVER'.
This is basically how my data looks like
Cust_Nr Date FilmName Watched Gladiator
157649306 20150430 Gladiator 1
158470722 20150504 Nick Cave: 20,000 Days On Earth 0
158467945 20150504 Out Of The Furnace 0
158470531 20150504 FilmA 0
157649306 20150510 Gladiator 1
158470722 20150515 Gladiator 1
I want to create a column (1 or zero) that shows if the customer has watched Gladiator then 1 ELSE 0. How can I do that?
I created a test column trying with a simple LIKE '%Gladiator%' THEN 1 ELSE 0. The problem with this solution is that it will show 1(one) more than once if the customer has watched multiple times. I only need 1 or zero.
I feel I am really close to finding a solution. I am very new to using OVER() and CASE WHEN but enjoying the thrill:=)

So you're saying that:
SELECT Cust_Nr, Date, FilmName,
CASE WHEN FilmName LIKE '%Gladiator%' THEN 1 ELSE 0 END as WatchedGladiator
FROM YourTable
WHERE YourColumn = #somevalue
Doesn't work? Because according to the data you've given, it should.
EDIT:
Well based on Tim's comment, I would simply add this bit to the query.
SELECT Cust_Nr, Date, FilmName, WatchedGladiator
FROM
(
SELECT Cust_Nr, Date, FilmName,
CASE WHEN FilmName LIKE '%Gladiator%' THEN 1 ELSE 0 END as WatchedGladiator
FROM YourTable
WHERE YourColumn = #somevalue
) as wg
WHERE WatchedGladiator = 1

The following does what you want for all films:
select r.*,
(case when row_number() over (partition by filmname order by date) = 1
then 1 else 0
end) as IsWatchedFirstAndGladiator
from results r;
For just Gladiator:
select r.*,
(case when filmname = 'Gladiator' and row_number() over (partition by filmname order by date) = 1
then 1 else 0
end) as IsWatchedFirst
from results r;

So you want to group by customer and add a column if this customer watched a specific film?
You could do:
SELECT Cust_Nr, MAX(Watched_Gladiator)
FROM( SELECT Cust_Nr,
Watched_Gladiator = CASE WHEN EXISTS
(
SELECT 1 FROM CustomerFilm c2
WHERE c2.Cust_Nr = c1.Cust_Nr
AND c2.FilmName LIKE '%Gladiator%'
) THEN 1 ELSE 0 END
FROM CustomerFilm c1 ) X
GROUP BY Cust_Nr
Demo
But it would be easier if you used the customer-table instead of this table, then you don't need the group-by.

Try grouping up to the cust/film level:
select
cust_nbr,
case when film_name like '%Gladiator%' then 1 else 0 end
from
(
select
cust_nbr,
film_name
from
<your table>
group by
cust_nbr,
film_name
) t
Or, as an alternative:
select distinct cust_nbr
from
<your table>
where
filmname = 'Gladiator'

Related

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'

select distinct with parent-child and return boolean for at least one child with a value?

I am currently searching for orders that have at least one orderline (product) with a certain boolean set:
- the product is a subscription product
- the product is a setup product
If one of the orderlines has this value set to 1, I want to return this in the query per DISTINCT order ID.
This does not seem to work for me:
SELECT DISTINCT [ORDER].[order_id]
,[ORDERLINE].[is_subscription] AS hasSubArticles
,[ORDERLINE].[is_setup] AS hasSetupArticles
FROM [ORDER]
LEFT JOIN [ORDERLINE]
ON [ORDER].[order_id] = [ORDERLINE].[f_order_id]
WHERE [G_ORDER].[status] = 1
ORDER BY [ORDER].[order_id]
,[ORDERLINE].[is_subscription] AS hasSubArticles
,[ORDERLINE].[is_setup] AS hasSetupArticles
When I check the returned records, I receive duplicate ORDER records:
order_id hasSubArticles hasSetupArticles
----------------------------------------
17804 NULL NULL
17804 1 0
I want to return only 1 record per order ID, thus this isn't working for me.
What am I doing wrong?
Distinct does not work for your requirement. MAX, Min functions are not allowed to use with bit type. You could use Group by and SUM like this
SELECT
[ORDER].[order_id]
,CASE WHEN SUM( CASE WHEN [ORDERLINE].[is_subscription] = 1 THEN 1 ELSE 0 END) > 0 THEN 1
ELSE 0
END AS hasSubArticles
,CASE WHEN SUM( CASE WHEN [ORDERLINE].[is_setup] = 1 THEN 1 ELSE 0 END) > 0
THEN 1
ELSE 0
END hasSetupArticles
FROM [ORDER]
LEFT JOIN [ORDERLINE]
ON [ORDER].[order_id] = [ORDERLINE].[f_order_id]
WHERE [G_ORDER].[status] = 1
GROUP BY [ORDER].[order_id]

How to pivot and get total amount each month

I have data like :
My table
My final results should be like this:
My SQL Statement:
SELECT 'Q'+cast([Month_Quarter] as varchar) Month_Quarter,COALESCE([Zugänge],0) Zugänge,COALESCE([Abgänge],0) Abgänge
FROM
(
SELECT DATEPART(QUARTER,[Monat]) [Month_Quarter],
[Zu-, Abgang],
Count(DISTINCT [Projektdefinition DB]) NoProjects
FROM AbZugänge
GROUP BY DATEPART(QUARTER,[Monat]), [Zu-, Abgang]
) proj
PIVOT (SUM(NoProjects) FOR [Zu-, Abgang] IN (Zugänge, Abgänge)) As pvt
WHERE [Month_Quarter] is not null
ORDER BY Month_Quarter
BUT with this statement I am getting the results without the Amount column Zugang and column Abgang:
How can I edit the statement to get the aggregation amount columns?
I suppose you can just wrap your query inside another select statement, then use GROUP BY. Something like this:
SELECT Month, SUM(ISNULL(column_name,0))
FROM (Your Query in here)
GROUP BY Month
Not sure I understand the point of the PIVOT in your original query. This looks like a typical aggregate is all that is required. See if this is what you need.
SELECT DATENAME(MONTH,Monat) [Month]
, sum(case when [Zu-, Abgang] = 'Zugänge' then [Zu-, Abgang] else 0 end) as Zugänge
, SUM(case when [Zu-, Abgang] = 'Abgänge' then [Zu-, Abgang] else 0 end) as Abgänge
, SUM([GWU aktuell]) as [GWU Total]
, SUM([GWU Planung aktuell]) AS [Plan Total]
, COUNT(DISTINCT [Projektdefinition DB]) NoProjects
FROM AbZugänge
group by DATENAME(MONTH,Monat)

SQL Percentage calculation

Is it possible in SQL to calculate the percentage of the 'StaffEntered' column's "Yes" values (case when calculated column) out of the grand total number of orders by that user (RequestedBy)? I'm basically doing this function now myself in Excel with a Pivot table, but thought it may be easier to build it into the query. Here is the existing sample SQL code:
Select
Distinct
RequestedBy = HStaff.Name,
AccountID = isnull(pv.AccountID, ''),
StaffEntered = Case When DictionaryItem2.Name like '%PLB%' Then 'Yes' Else 'No' end
FROM
[dbo].[HOrd] HOrd WITH ( NOLOCK )
left outer join HStaff HStaff with (nolock)
on HOrd.Requestedby_oid = HStaff.ObjectID
and HStaff.Active = 1
left outer join DictionaryItem DictionaryItem2 WITH (NOLOCK)
ON HSUser1.PreferenceGroup_oid = DictionaryItem2.ObjectID
AND DictionaryItem2.ItemType_oid = 98
Here is what I am doing in Excel currently with the query results, I have a pivot table and I am dividing the "Yes" values of the "StaffEntered" field out of the Grand Total number of entries for that specific "RequestedBy" user. Essentially Excel is doing the summarization and then I am doing a simple division calculation to obtain the percentage.
Thanks in advance!
You didn't provide a lot in the way of details but I think this should be pretty close to what you are looking for.
select HStaff.Name as RequestedBy
, isnull(pv.AccountID, '') as AccountID
, Case When DictionaryItem2.Name like '%PLB%' Then 'Yes' Else 'No' end as StaffEntered
, sum(Case When DictionaryItem2.Name like '%PLB%' Then 1 Else 0 end) / GrandTotal
From SomeTable
group by HStaff.Name
, isnull(pv.AccountID, '')
, GrandTotal
Giving the FROM part of your SQL Statement would allow us to create a more correct answer. This statement will get the totals of yes/no per HStaff name and add it to each detail record in your SQL statement:
WITH cte
AS ( SELECT HStaff.Name ,
SUM(CASE WHEN dictionaryItem2.Name LIKE '%PLB%' THEN 1
ELSE 0
END) AS YesCount ,
SUM(CASE WHEN dictionaryItem2.Name NOT LIKE '%PLB%'
THEN 1
ELSE 0
END) AS NotCount
FROM YourTable
GROUP BY HStaff.Name
)
SELECT HStaff.Name AS requestedBy ,
ISNULL(pv.AccountID, '') AS AccountID ,
CASE WHEN DictionaryItem2.Name LIKE '%PLB%' THEN 'Yes'
ELSE 'No'
END AS StaffEntered ,
cte.YesCount / ( cte.YesCount + cte.NotCount ) AS PLB_Percentage
FROM yourtable
INNER JOIN cte ON yourtable.Hstaff.Name = cte.NAME

Subquery in CTE

i'm trying to get the status of student after the semester ends
and then update the status in it's table
can i use subquery inside the CTE ??
;with temp As
(
select CASE WHEN Status = 0 then 'Passed'
when status >0 and status < 2 then 'uncomplete'
else 'Failed' end Studentstatus
from
(
SELECT StudentID,
sum(CASE WHEN CourseStatus =1 then 1 else 0 end) Status
FROM StudentFinalResultsDetails
group by StudentID
)As t
)--the error in this line
the problem is
Incorrect syntax near ')'
You don't need a subquery for this, either use a CTE or subquery; you are mixing both of them, just do this:
with temp As
(
SELECT StudentID,
sum(CASE WHEN CourseStatus =1 then 1 else 0 end) Status
FROM StudentFinalResultsDetails
group by StudentID
) -- You have to select something after the brackets
select CASE WHEN Status = 0 then 'Passed'
when status >0 and status < 2 then 'uncomplete'
else 'Failed' end AS Studentstatus
from temp
Or: remove WITH CTE:
select CASE WHEN Status = 0 then 'Passed'
when status >0 and status < 2 then 'uncomplete'
else 'Failed' end Studentstatus
from
(
SELECT StudentID,
sum(CASE WHEN CourseStatus =1 then 1 else 0 end) Status
FROM StudentFinalResultsDetails
group by StudentID
)As t
Update
The problem in the query in your question, you have to move the part:
select CASE WHEN Status = 0 then 'Passed'
when status >0 and status < 2 then 'uncomplete'
else 'Failed' end AS Studentstatus`
to the outer of the brackets of the WITH temp ( .... ) then after it select whatever you want from it.
Because:
A CTE must be followed by a single SELECT, INSERT, UPDATE, or DELETE
statement that references some or all the CTE columns. A CTE can also
be specified in a CREATE VIEW statement as part of the defining SELECT
statement of the view.
In your query, you didn't put any statements after it. See reference

Resources