SQL procedure select - sql

I would like to write a procedure to database which will return select all data from database Tournaments plus bool parameter. If user is registered, it will return true.
Call:
exec TournamentsWithLoggedUser #user = 'asd123'
Procedure:
CREATE PROCEDURE [dbo].[TournamentsWithLoggedUser]
#user nvarchar(128)
AS
SELECT
t.Id, t.Info, BIT(r.Id)
FROM
Tournaments AS t
LEFT JOIN
Registrations AS r ON t.Id = r.TournamentId
WHERE
r.UserId IS NULL OR r.UserId = #user
RETURN
it mean something like
1, 'some info', true //1
2, 'some info2', false //2

Why not just use a case statement?
CASE WHEN r.Id IS NULL THEN 0 ELSE 1 END
Change the 0 and 1 to whatever you want for false and true.

SELECT t.Id, t.Info,
-- this works in SQL Server
CAST ((CASE WHEN r.UserId IS NOT NULL THEN 1 ELSE 0 END) AS BIT) AS IsRegistered
FROM Tournaments as t
LEFT JOIN Registrations as r ON t.Id = r.TournamentId
where (r.UserId = '' OR r.UserId = #user)
-- i think this one is help for you...

You are looking for this query
SELECT t.id,
t.info,
Cast (CASE
WHEN r.userid IS NOT NULL THEN 1
ELSE 0
END AS BIT) AS IsRegistered
FROM tournaments AS t
LEFT JOIN registrations AS r
ON t.id = r.tournamentid
AND r.userid = #user

You should clarify what SQL language you are actually using, but an answer can be provided anyway:
CREATE PROCEDURE [dbo].[TournamentsWithLoggedUser]
#user nvarchar(128)
AS
BEGIN
SELECT t.Id, t.Info,
-- this works in SQL Server
CAST ((CASE WHEN r.UserId IS NOT NULL THEN 1 ELSE 0 END) AS BIT) AS IsRegistered
FROM Tournaments as t
LEFT JOIN Registrations as r ON t.Id = r.TournamentId
where r.UserId IS NULL OR r.UserId = #user
-- this should not be required
RETURN
END
However, there is a problem with the logic:
#user is not nullable, so your procedure gives the impression that it looks data for a single user. However, your OR operator allows to select all records from unregistered users united with the record for the particular provided user (if exists).

Related

In stored procedure not getting expected result

I have 3 tables Surveys, RightsonSurveys and Users.
I want the result as shown in the screenshot below where all survey list should be shown but only those surveys should be checked where user has IsViewer or IsAuthor is true:
But instead, I'm getting this result:
In the RightsOnSurveys table I am providing permissions to access particular surveys. For this I have created a view like this:
ALTER VIEW [dbo].[vRightsOnSurveys]
AS
SELECT
ID SurveyID, 0 ID, -1 UserID,
CAST(1 AS bit) IsViewer, CAST(1 AS bit) IsAuthor, Name
FROM
Surveys
UNION
SELECT
SurveyID, ID, UserID, IsViewer, IsAuthor, NULL AS Name
FROM
RightsOnSurveys
GO
SELECT * FROM [dbo].[vRightsOnSurveys]
Here is output of this view:
and selecting from this view in the stored procedure for front end where SurveyID and UserID could be null or have data
ALTER PROCEDURE [dbo].[GetUserRightsOnSurveys]
(#SurveyID int = NULL,
#UserID int = NULL)
AS
BEGIN
SELECT
ID, UserID, SurveyID, ros.IsViewer, IsAuthor, ros.Name
FROM
vRightsOnSurveys ros
WHERE
(ros.SurveyID = #SurveyID OR #SurveyID IS NULL)
AND (ros.UserID = #UserID OR #UserID IS NULL)
END
With this procedure, I am not getting results as per my requirements.
Can somebody please help me? Thanks in advance..
You need left join in the view as follows:
alter view [dbo].[vRightsOnSurveys]
as
select s.ID SurveyID,
coalesce(r.id,0) ID,
coalesce(r.userid,-1) UserID,
coalesce(r.isviewer,cast(1 as bit)) IsViewer,
coaleace(r.isauthor,cast(1 as bit)) IsAuthor,
s.Name
from Surveys s
left Join rightonsurvey r on r.surveyid = s.id;
I have to change my whole query and got my results as per my requirements here is the query..
select usr.ID as UserID,usr.Login,usr.Name as UserName, sur.ID as SurveyID,sur.Name
,ISNULL((select r.ID from RightsOnSurveys r where r.UserID = usr.ID and r.SurveyID
= sur.ID),0) as ID
,ISNULL((select r.IsViewer from RightsOnSurveys r where r.UserID = usr.ID and
r.SurveyID = sur.ID),cast(0 as bit)) as IsViewer
,ISNULL((select r.IsAuthor from RightsOnSurveys r where r.UserID = usr.ID and
r.SurveyID = sur.ID),cast(0 as bit)) as IsAuthor
from Users usr,Surveys sur

SQL Query takes too much time when retrieving data

select
*
from
[platformnew].[dbo].[users] u
where u.Phone not in
(select case
when convert(varchar(100),phonenumber) <> '' then convert(varchar(100),ISNULL(PhoneNumber,0))
else convert(varchar(100),0)
end phonenumber
from
[ecommerce_crm].[dbo].[mpcustomer]
)
and u.Phone not in
(select case
when convert(varchar(100),phonenumber2) <> '' then convert(varchar(100),ISNULL(PhoneNumber2,0))
else convert(varchar(100),0)
end phonenumber2
from [ecommerce_crm].[dbo].[mpcustomer]
)
and u.Email not in
(select ISNULL(Email,'')
from [ecommerce_crm].[dbo].[mpcustomer])
and u.Email not in
(select ISNULL(Email2,'')
from [ecommerce_crm].[dbo].[mpcustomer])
this query takes too much time to execute,i think it takes around 3 more minutes,please help me to solve it
you need to change the Not in to not Exists like this for example
select
*
from
[platformnew].[dbo].[users] u
where u.Phone not exists
(select 1 from
[ecommerce_crm].[dbo].[mpcustomer]
WHERE convert(varchar(100),ISNULL([ecommerce_crm].[dbo].[mpcustomer].PhoneNumber,0)) = u.Phone
)
and for the convert part you only need isnull convert(varchar(100),ISNULL([ecommerce_crm].[dbo].[mpcustomer].PhoneNumber,0))
just remove the case when
You should see performance increase when you change all NOT IN clauses to NOT EXISTS. E.g:
and not exists
(
select 1
from [ecommerce_crm].[dbo].[mpcustomer] mpc
where u.Phone = mpc.Phone
)
Also, all CONVERT functions are unnecessary. If phonenumber data type is int, then write your clauses using this type, e.g isnull(phonenumber,0)<>0. And if phone numbers should not be null or 0, add it as separate conditions, because now every time when it's null or 0, you unnecessarily look up other tables to find null and 0 there to discard it.
SELECT *
FROM [platformnew].[dbo].[users] u
WHERE NOT EXISTS (
SELECT 1
FROM [ecommerce_crm].[dbo].[mpcustomer] c
WHERE c.phonenumber = u.Phone
OR c.phonenumber2 = u.Phone
OR c.Email = u.Email
OR c.Email2 = u.Email
)
AND u.Phone != '0'
AND u.Email != ''

How can I perform the Count function with a where clause?

I have my database setup to allow a user to "Like" or "Dislike" a post. If it is liked, the column isliked = true, false otherwise (null if nothing.)
The problem is, I am trying to create a view that shows all Posts, and also shows a column with how many 'likes' and 'dislikes' each post has. Here is my SQL; I'm not sure where to go from here. It's been a while since I've worked with SQL and everything I've tried so far has not given me what I want.
Perhaps my DB isn't setup properly for this. Here is the SQL:
Select trippin.AccountData.username, trippin.PostData.posttext,
trippin.CategoryData.categoryname, Count(trippin.LikesDislikesData.liked)
as TimesLiked from trippin.PostData
inner join trippin.AccountData on trippin.PostData.accountid = trippin.AccountData.id
inner join trippin.CategoryData on trippin.CategoryData.id = trippin.PostData.categoryid
full outer join trippin.LikesDislikesData on trippin.LikesDislikesData.postid =
trippin.PostData.id
full outer join trippin.LikesDislikesData likes2 on trippin.LikesDislikesData.accountid =
trippin.AccountData.id
Group By (trippin.AccountData.username), (trippin.PostData.posttext), (trippin.categorydata.categoryname);
Here's my table setup (I've only included relevant columns):
LikesDislikesData
isliked(bit) || accountid(string) || postid(string
PostData
id(string) || posttext || accountid(string)
AccountData
id(string) || username(string)
CategoryData
categoryname(string)
Problem 1: FULL OUTER JOIN versus LEFT OUTER JOIN. Full outer joins are seldom what you want, it means you want all data specified on the "left" and all data specified on the "right", that are matched and unmatched. What you want is all the PostData on the "left" and any matching Likes data on the "right". If some right hand side rows don't match something on the left, then you don't care about it. Almost always work from left to right and join results that are relevant.
Problem 2: table alias. Where ever you alias a table name - such as Likes2 - then every instance of that table within the query needs to use that alias. Straight after you declare the alias Likes2, your join condition refers back to trippin.LikesDislikesData, which is the first instance of the table. Given the second one in joining on a different field I suspect that the postid and accountid are being matched on the same row, therefore it should be AND together, not a separate table instance. EDIT reading your schema closer, it seems this wouldn't be needed at all.
Problem 3: to solve you Counts problem separate them using CASE statements. Count will add the number of non NULL values returned for each CASE. If the likes.liked = 1, then return 1 otherwise return NULL. The NULL will be returned if the columns contains a 0 or a NULL.
SELECT trippin.PostData.Id, trippin.AccountData.username, trippin.PostData.posttext,
trippin.CategoryData.categoryname,
SUM(CASE WHEN likes.liked = 1 THEN 1 ELSE 0 END) as TimesLiked,
SUM(CASE WHEN likes.liked = 0 THEN 1 ELSE 0 END) as TimesDisLiked
FROM trippin.PostData
INNER JOIN trippin.AccountData ON trippin.PostData.accountid = trippin.AccountData.id
INNER JOIN trippin.CategoryData ON trippin.CategoryData.id = trippin.PostData.categoryid
LEFT OUTER JOIN trippin.LikesDislikesData likes ON likes.postid = trippin.PostData.id
-- remove AND likes.accountid = trippin.AccountData.id
GROUP BY trippin.PostData.Id, (trippin.AccountData.username), (trippin.PostData.posttext), (trippin.categorydata.categoryname);
Then "hide" the PostId column in the User Interface.
Instead of selecting Count(trippin.LikesDislikesData.liked) you could put in a select statement:
Select AccountData.username, PostData.posttext, CategoryData.categoryname,
(select Count(*)
from LikesDislikesData as likes2
where likes2.postid = postdata.id
and likes2.liked = 'like' ) as TimesLiked
from PostData
inner join AccountData on PostData.accountid = AccountData.id
inner join CategoryData on CategoryData.id = PostData.categoryid
USE AdventureWorksDW2008R2
GO
SET NOCOUNT ON
GO
/*
Default
*/
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
GO
BEGIN TRAN
IF OBJECT_ID('tempdb.dbo.#LikesDislikesData') IS NOT NULL
BEGIN
DROP TABLE #LikesDislikesData
END
CREATE TABLE #LikesDislikesData(
isLiked bit
,accountid VARCHAR(50)
,postid VARCHAR(50)
);
IF OBJECT_ID('tempdb.dbo.#PostData') IS NOT NULL
BEGIN
DROP TABLE #PostData
END
CREATE TABLE #PostData(
postid INT IDENTITY(1,1) NOT NULL
,accountid VARCHAR(50)
,posttext VARCHAR(50)
);
IF OBJECT_ID('tempdb.dbo.#AccountData') IS NOT NULL
BEGIN
DROP TABLE #AccountData
END
CREATE TABLE #AccountData(
accountid INT
,username VARCHAR(50)
);
IF OBJECT_ID('tempdb.dbo.#CategoryData') IS NOT NULL
BEGIN
DROP TABLE #CategoryData
END
CREATE TABLE #CategoryData(
categoryname VARCHAR(50)
);
INSERT INTO #AccountData VALUES ('1', 'user1')
INSERT INTO #PostData VALUES('1','this is a post')
INSERT INTO #LikesDislikesData (isLiked ,accountid, postid)
SELECT '1', P.accountid, P.postid
FROM #PostData P
WHERE P.posttext = 'this is a post'
SELECT *
FROM #PostData
SELECT *
FROM #LikesDislikesData
SELECT *
FROM #AccountData
SELECT COUNT(L.isLiked) 'Likes'
,P.posttext
,A.username
FROM #PostData P
JOIN #LikesDislikesData L
ON P.accountid = L.accountid
AND L.IsLiked = 1
JOIN #AccountData A
ON P.accountid = A.accountid
GROUP BY P.posttext, A.username
SELECT X.likes, Y.dislikes
FROM (
(SELECT COUNT(isliked)as 'likes', accountid
FROM #LikesDislikesData
WHERE isLiked = 1
GROUP BY accountid
) X
JOIN
(SELECT COUNT(isliked)as 'dislikes', accountid
FROM #LikesDislikesData
WHERE isLiked = 0
GROUP BY accountid) Y
ON x.accountid = y.accountid)
IF (XACT_STATE() = 1 AND ERROR_STATE() = 0)
BEGIN
COMMIT TRAN
END
ELSE IF (##TRANCOUNT > 0)
BEGIN
ROLLBACK TRAN
END
How do you think about the solution? We create a new table SummaryReport(PostID,AccountID,NumberOfLikedTime,NumberOfDislikedTimes).
An user clicks on LIKE or DISLIKE button we update the table. After that, you can query as you desire. Another advantage, the table can be served reporting purpose.

SQL update rows in column using CASE statement

I have two tables, Users and #TempTable (which is a subset of Users). I would like to update a column, IsActive, in the Users table. I would like to set IsActive = 1 if a user that is in #TempTable is also in the Users table, and set IsActive = 0 otherwise.
Getting the users from Users that are NOT in #TempTable (IsActive should be set to 0 for these users):
-- (Users \ #TempTable) U (#TempTable \ Users)
SELECT u.UserName
FROM Users u
WHERE (u.UserName) NOT IN
(SELECT t.[User Name] FROM #TempTable t)
UNION ALL
SELECT t.[User Name]
FROM #TempTable t
WHERE (t.[User Name]) NOT IN
(SELECT u.UserName FROM Users u)
Let's call this the ResultSet. I would appreciate some help with my UPDATE statement. What I'd like to be able to do is:
UPDATE Users
SET IsActive = (CASE WHEN User.UserName IN ResultSet THEN 0 ELSE 1 END)
without having to write out the CASE WHEN for each User.UserName. Thanks in advance!
You can use a join in the UPDATE statement.
UPDATE Users
SET Users.Active = CASE WHEN T.UserName is null THEN 0 ELSE 1 END
FROM Users AS U
LEFT JOIN #TempTable AS T ON U.UserName = T.UserName
Notes :
You could also use a sub-query but that would be much slower (order of n squared not order of n). For a small number of users this would not matter.
I did not test so I could have a typo / bug in the code above.
Based on crazy comments about how this would not work I implemented a fiddle.
Enjoy it working:
http://sqlfiddle.com/#!6/25235/3

Are both queries the same?

Are both queries are same ?
Do both return the same result ?
1)
IF EXISTS(
SELECT
1
FROM
Users u
WHERE
u.UPIN = #AttendingDoctorID)
BEGIN
SELECT
u.UserId, 1
FROM
Users u WITH(nolock)
WHERE
u.UPIN = #AttendingDoctorID
END ELSE BEGIN
SELECT
u.UserId,
1
FROM
Users u (nolock)
WHERE
u.FirstName = #AttendingDoctorFirstName AND
u.LastName = #AttendingDoctorLastName
END
2)
SELECT
u.UserId, 1
FROM
Users u (nolock)
WHERE
(u.UPIN = #AttendingDoctorID)
OR
(u.FirstName = #AttendingDoctorFirstName AND
u.LastName = #AttendingDoctorLastName)
They are not the same.
The 2nd returns data for both conditions.
The 1st one tests first and applies only one condition
They're not semantically the same. The second query will possibly return records that fulfill both predicates (u.UPIN = #AttendingDoctorID) and (u.FirstName = #AttendingDoctorFirstName AND u.LastName = #AttendingDoctorLastName).
Whether or not this will ever occur depends on your data.
Assuming you're running under the default transaction isolation level, you also need to be aware that:
IF EXISTS(
SELECT
1
FROM
Users u
WHERE
u.UPIN = #AttendingDoctorID) --<-- Query 1
BEGIN
SELECT
u.UserId, 1
FROM
Users u WITH(nolock)
WHERE
u.UPIN = #AttendingDoctorID --<-- Query 2
END ELSE BEGIN
SELECT
u.UserId,
1
FROM
Users u (nolock)
WHERE
u.FirstName = #AttendingDoctorFirstName AND
u.LastName = #AttendingDoctorLastName
END
Another transaction might update Users between query 1 executing and query 2 executing, and so you might get an empty result set from query 2. Your second version runs everything as a single query, so will not have this issue (but others have pointed out other differences between the queries)