I have this kind of query in my ms sql.
SELECT
OdeaMainDataBase.dbo.ItemChoicesTable.*,
CASE
WHEN EXISTS(SELECT * FROM StudentTestTablefromRemark
WHERE StudID = #studId AND ExamID = #examId
AND ItemBankID = #itemBankId
AND ChoiceID = OdeaMainDataBase.dbo.ItemChoicesTable.ChoiceID)
THEN 'True'
ELSE 'False'
END AS isAnswered
FROM
OdeaMainDataBase.dbo.ItemChoicesTable
WHERE
ItemBankID = #itemBankId
I observe that the CASE statement I made causes my query to run slow. When I remove that CASE, it run fast but I need that case for my output.
Here is the structure I want to display
Item Choice Table
ItemBankID ChoiceID ChoiceLetter
--------------------------------------
1 1 A
1 2 B
1 3 C
2 4 A
2 5 B
2 6 C
Student Answers Table
ItemBankID ChoiceID ChoiceLetter StudentId
-------------------------------------------------
1 2 B 123
2 6 C 123
Desired Output
ItemBankID ChoiceID ChoiceLetter IsAnswered
----------------------------------------------------
1 1 A False
1 2 B True
1 3 C False
2 4 A False
2 5 B False
2 6 C True
I want to achieve that output without slowing my program.
Any idea will do to me.
Thank you so much
Try this
SELECT i.*,
CASE
WHEN s.ChoiceID IS NULL THEN 'False'
ELSE 'True'
END
FROM OdeaMainDataBase.dbo.ItemChoicesTable i
LEFT OUTER JOIN StudentTestTablefromRemark s
ON s.ChoiceID = i.ChoiceID
AND s.ItemBankID = i.ItemBankID
AND s.StudID = #studId
AND s.ExamID = #examId
WHERE i.ItemBankID = #itemBankId
Try this query
SELECT OdeaMainDataBase.dbo.ItemChoicesTable.*,
ISNULL(a,'False') as isAnswered
FROM OdeaMainDataBase.dbo.ItemChoicesTable
left outer join(Select 'True' as a,ChoiceID FROM StudentTestTablefromRemark
Where StudID=#studId AND ExamID=#examId AND ItemBankID=#itemBankId )b on b.ChoiceID=OdeaMainDataBase.dbo.ItemChoicesTable.ChoiceID
Where ItemBankID = #itemBankId
Related
Could somebody please help me create a view in SQL server to get a percentage in a new column. For example I have two tables as below.
Table 1---> Subject | Table 2---->Exam
|
SubjectID SubName | ExamID SubjectID Result (bool)
1 Science | 1 1 1
2 Maths | 2 1 1
3 English | 3 1 0
4 History | 4 2 0
5 Art | 5 2 1
6 Geography | 6 3 0
| 7 4 1
|
As you can see, many subjects do not have exams hence result will be null in a joined view. I want to show the pass percentage of a subject. For example, in result column, 1 = pass, 0 = Fail. I want the result to look like below showing null fields as well.
SubjectID SubName PassPercentage
1 Science 66.66
2 Maths 50
3 English 0
4 History 100
5 Art null
6 Geography null
Here:
SELECT
Subject.SubjectId,
Subject.SubName,
(AVG(CONVERT(decimal,Exam.Result))*100) AS PassPercentage
FROM Subject
LEFT JOIN Exam on Subject.SubjectId = Exam.SubjectId
GROUP BY Subject.SubjectId, Subject.SubName
You can round percentage result (2 or without decimals) and add % sign if you want.
Use this query:
Select *,
(Select Avg(Cast(Result as decimal)) From Exam Where SubjectID=S.SubjectID)*100 as PassPercentage
From Subject as S
Result is:
SubjectID SubName PassPercentage
----------- --------------- ---------------------------------------
1 Science 66.666600
2 Maths 50.000000
3 English 0.000000
4 History 100.000000
5 Art NULL
6 Geography NULL
(6 row(s) affected)
Subquery will be executed for each row of subject table.
do:
select s.SubjectID, s.SubName,
case COUNT(e.Result)
when 0 then null
else SUM(CAST(e.Result AS INT)) * 100 / (COUNT(s.SubjectID))
end PassPrec
from Subject s
left join Exam e on s.SubjectID = e.SubjectID
group by s.SubjectID, s.SubName
there case is to get the null. nulls don't get counted in "count" function.
you can use code like this
;with cte1 as (
select subjectid, sum(result) ResultCount
from exam group by subjectid
), cte2 as (
select e.subjectid, c.ResultCount, count(e.examid) TotalExams from cte1 c
left join exam e on e.subjectid = c.subjectid
group by e.subjectid, c.ResultCount
) select s.subname, convert(decimal(10,2), (c.ResultCount/convert(decimal(10,2),c.TotalExams)) *100) as Percentage from subject s left join cte2 c
on s.subjectid = c.subjectid
We have a table with a parent child relationship, that represents a deep tree structure.
We are using a view with a CTE to query the data but the performance is poor (see code and execution plan below).
Is there any way we can improve the performance?
WITH cte (ParentJobTypeId, Id) AS
(
SELECT
Id, Id
FROM
dbo.JobTypes
UNION ALL
SELECT
e.Id, cte.Id
FROM
cte
INNER JOIN
dbo.JobTypes AS e ON e.ParentJobTypeId = cte.ParentJobTypeId
)
SELECT
ISNULL(Id, 0) AS ParentJobTypeId,
ISNULL(ParentJobTypeId, 0) AS Id
FROM
cte
A quick example of using the range keys. As I mentioned before, hierarchies were 127K points and some sections where 15 levels deep
The cte Builds, let's assume the hier results will be will be stored in a table (indexed as well)
Declare #Table table(ID int,ParentID int,[Status] varchar(50))
Insert #Table values
(1,101,'Pending'),
(2,101,'Complete'),
(3,101,'Complete'),
(4,102,'Complete'),
(101,null,null),
(102,null,null)
;With cteOH (ID,ParentID,Lvl,Seq)
as (
Select ID,ParentID,Lvl=1,cast(Format(ID,'000000') + '/' as varchar(500)) from #Table where ParentID is null
Union All
Select h.ID,h.ParentID,cteOH.Lvl+1,Seq=cast(cteOH.Seq + Format(h.ID,'000000') + '/' as varchar(500)) From #Table h INNER JOIN cteOH ON h.ParentID = cteOH.ID
),
cteR1 as (Select ID,Seq,R1=Row_Number() over (Order by Seq) From cteOH),
cteR2 as (Select A.ID,R2 = max(B.R1) From cteOH A Join cteR1 B on (B.Seq Like A.Seq+'%') Group By A.ID)
Select B.R1
,C.R2
,A.Lvl
,A.ID
,A.ParentID
Into #TempHier
From cteOH A
Join cteR1 B on (A.ID=B.ID)
Join cteR2 C on (A.ID=C.ID)
Select * from #TempHier
Select H.R1
,H.R2
,H.Lvl
,H.ID
,H.ParentID
,Total = count(*)
,Complete = sum(case when D.Status = 'Complete' then 1 else 0 end)
,Pending = sum(case when D.Status = 'Pending' then 1 else 0 end)
,PctCmpl = format(sum(case when D.Status = 'Complete' then 1.0 else 0.0 end)/count(*),'##0.00%')
From #TempHier H
Join (Select _R1=B.R1,A.* From #Table A Join #TempHier B on A.ID=B.ID) D on D._R1 between H.R1 and H.R2
Group By H.R1
,H.R2
,H.Lvl
,H.ID
,H.ParentID
Order By 1
Returns the hier in a #Temp table for now. Notice the R1 and R2, I call these the range keys. Data (without recursion) can be selected and aggregated via these keys
R1 R2 Lvl ID ParentID
1 4 1 101 NULL
2 2 2 1 101
3 3 2 2 101
4 4 2 3 101
5 6 1 102 NULL
6 6 2 4 102
VERY SIMPLE EXAMPLE: Illustrates the rolling the data up the hier.
R1 R2 Lvl ID ParentID Total Complete Pending PctCmpl
1 4 1 101 NULL 4 2 1 50.00%
2 2 2 1 101 1 0 1 0.00%
3 3 2 2 101 1 1 0 100.00%
4 4 2 3 101 1 1 0 100.00%
5 6 1 102 NULL 2 1 0 50.00%
6 6 2 4 102 1 1 0 100.00%
The real beauty of the the range keys, is if you know an ID, you know where it exists (all descendants and ancestors).
Consider a Sample Table with two Column RoleId and User Name
Role | Name
1 AB
3 A
1 ABC
2 D
2 B
3 Abb
1 E
4 TE
How can i use SQL queries to get following Output.
Role | Name
3 A
3 Abb
1 AB
1 ABC
1 E
2 B
2 D
4 TE
I just want to Order by Role Id 3 first then by remaining Roleid.
Currently i am using Union to achieve so //
SELECT * FROM (SELECT * From #temp
Where roleid=3
UNION ALL
SELECT * From #temp
Where roleid != 3
) as X
You can use case to make more complex ordering:
select *
from #temp
order by case when Role = 3 then 0 else 1 end, Role, Name
select *
from #temp
order by CASE WHEN Role = 3 THEN 0 ELSE Role END, Name
I usually use NULLIF, but case might be faster?
SELECT *
FROM #temp
ORDER BY NULLIF(Role,3), Name
Select *
From cacheAttendanceMeasures cam
Left Join dmcUserSelectedAttendanceMeasures usam on usam.attMeasureID = cam.attMeasureID
And usam.personID = #personID
And usam.pageID = #pageID
I understand that the above query will return all rows from cacheAttendanceMeasures as well as any rows from dmcUserSelectedAttendanceMeasures and dmcUserSelectedStudentMonths where the conditions match. What I would really like it to do is this:
If the left join(s) don't match, then return all rows from cacheAttendanceMeasures -- so what it is currently doing
However, if the left join(s) DO match, then return ONLY the rows that match from cacheAttendanceMeasures
Is this possible?
EDIT:
I simplified the query above to only include one Left Join table, I don't want to over-complicate the issue.
Below are dataset examples I'd like to see returned based on if there are no matches between the tables versus there is a match:
cam Table
cID attMeasureID Value
1 1 530
2 2 95.7
3 3 380
4 4 742.57
5 5 200
usam Table
uID personID pageID attMeasureID
1 877450 31 1
2 923450 28 2
3 877450 31 3
4 369842 28 4
5 212193 25 1
Dataset to Return if #personID = 577597 & #pageID = 20:
CID attMeasureID Value uID pageID attMeasureID
1 1 530 null nul null
2 2 95.7 null null null
3 3 380 null null null
4 4 742.57 null null null
5 5 200 null null null
Dataset to Return if #personID = 877450 & #pageID = 31:
CID attMeasureID Value uID personID pageID attMeasureID
1 1 530 1 877450 31 1
3 3 380 3 877450 31 3
I'm not really sure what results you want...
At first I thought maybe something like this..
SELECT *
FROM cacheAttendanceMeasures cam
LEFT JOIN dmcUserSelectedAttendanceMeasures usam
on usam.attMeasureID = cam.attMeasureID
And usam.personID = #personID
And usam.pageID = #pageID
LEFT JOIN dmcUserSelectedStudentMonths ussm
on ussm.monthSeq = cam.pupilMonth
And ussm.personID = USAM.PersonID
And ussm.pageID = USAM.PageId
--This where clause would seem silly as it negates the left joins; making them inner joins and violates #1 as to what you're after. so I don't think that's what you're after...
WHERE ussm.personID is not null
and usam.personID is not null
So with rule 2 are you saying... if a single record isn't null, then only return records which exist in both Left joined tables and the 1st table?
So given:
T1 T2 T3
X X X
Y NULL Y
Z Y NULL
T NULL NULL
I think you would want just record X
But Given
So given:
T1 T2 T3
X X NULL
Y NULL Y
Z Y NULL
T NULL NULL
I think you would want X, Y Z, and T... need better clairification.
I do not quite understand why those two different codesamples return a different value.
somehow incorrect but working syntax, returns false results, e.g it returns 0 when the comparison is done over two equal values:
(SELECT CASE
WHEN
SUM(V.IsCompatible) OVER
(PARTITION BY ComputerName, UserID) = ApplicationCount
THEN 1 ELSE 0 END
) AS CompatibleUser
The one below returns the correct values, ie. 1 when there are two equal values compared.
(CASE
WHEN
SUM(V.IsCompatible) OVER
(PARTITION BY ComputerName, UserID) = ApplicationCount
THEN 1 ELSE 0 END
) AS CompatibleUser
or even simpler:
(SELECT CASE
WHEN
X = Y
THEN 1 ELSE 0 END
) AS Result
X = 22 AND Y = 22 => Result = 0
(CASE
WHEN
X = Y
THEN 1 ELSE 0 END
) AS Result
X = 22 AND Y = 22 => Result = 1
I understand applying the correct syntax is important, and I am aware of the SELECT CASE syntax in T-SQL, but I do not understand how the first code sample is evaluated and delivering an unexpected result.
update: full query in it's context
select userapplication.username,
computerdetails.computername,
sum(userapplication.iscompatible)
over (partition by computerdetails.computername,
userapplication.userid) as compatiblecount,
userapplication.applicationcount,
( case
when sum(userapplication.iscompatible)
over (partition by
computerdetails.computername,
userapplication.userid) <> userapplication.applicationcount
then 0
else 1
end
) as usercomputeriscompatible
from computerdetails
right outer join usercomputer
on computerdetails.computerid = usercomputer.computerid
right outer join userapplication
on usercomputer.gebruikerid = userapplication.userid
so userComputerIsCompatible is the result in question here
I think the reason for this behavior is the next one: the expressions like (SELECT ...) are considered to be sub-queries even they don't have FROM clause. Is assume the source of data for these (false) "sub-queries" is only the current row. So, (SELECT expression) is interpreted as (SELECT expression FROM current_row) and (SELECT SUM(iscompatible)OVER(...)) is executed as (SELECT SUM(iscompatible)OVER(current_row)).
Argument: analyzing execution plan for (SELECT SUM(IsWeb) OVER(PARTITION BY OrderDate) [FROM current_row]) expression
I see a Constant Scan (Scan an internal table of constants) operator instead of Clustered Index Scan before Segment and Stream Aggregate ([Expr1007] = Scalar Operator(SUM(#OrderHeader.[IsWeb] as [h].[IsWeb]))) operators. This internal table (Constant Scan) is constructed from current row.
Example (tested with SQL2005SP3 and SQL2008):
DECLARE #OrderHeader TABLE
(
OrderHeaderID INT IDENTITY PRIMARY KEY
,OrderDate DATETIME NOT NULL
,IsWeb TINYINT NOT NULL --or BIT
);
INSERT #OrderHeader
SELECT '20110101', 0
UNION ALL
SELECT '20110101', 1
UNION ALL
SELECT '20110101', 1
UNION ALL
SELECT '20110102', 1
UNION ALL
SELECT '20110103', 0
UNION ALL
SELECT '20110103', 0;
SELECT *
,SUM(IsWeb) OVER(PARTITION BY OrderDate) SumExpression_1
FROM #OrderHeader h
ORDER BY h.OrderDate;
SELECT *
,(SELECT SUM(IsWeb) OVER(PARTITION BY OrderDate)) SumWithSubquery_2
FROM #OrderHeader h
ORDER BY h.OrderDate;
Results:
OrderHeaderID OrderDate IsWeb SumExpression_1
------------- ----------------------- ----- ---------------
1 2011-01-01 00:00:00.000 0 2
2 2011-01-01 00:00:00.000 1 2
3 2011-01-01 00:00:00.000 1 2
4 2011-01-02 00:00:00.000 1 1
5 2011-01-03 00:00:00.000 0 0
6 2011-01-03 00:00:00.000 0 0
OrderHeaderID OrderDate IsWeb SumWithSubquery_2
------------- ----------------------- ----- -----------------
1 2011-01-01 00:00:00.000 0 0
2 2011-01-01 00:00:00.000 1 1
3 2011-01-01 00:00:00.000 1 1
4 2011-01-02 00:00:00.000 1 1
5 2011-01-03 00:00:00.000 0 0
6 2011-01-03 00:00:00.000 0 0
I tried your code and I get the same results for both queries. Here's the code I tried:
DECLARE #X INT = 22
DECLARE #Y INT = 22
SELECT (SELECT CASE
WHEN
#X = #Y
THEN 1 ELSE 0 END
) AS Result
SELECT (CASE
WHEN
#X = #Y
THEN 1 ELSE 0 END
) AS Result
Result is 1 and 1
I ran this on SQL Server 2008 R2