SQL get single value inside existing query? - sql

I have a query that returns a bunch of rows.
But using the same query i would like to:
1. get the total row count in the table
2. get the row number where a certian username is located
Right now im doing like so:
BEGIN
DECLARE #startRowIndex INT;
DECLARE #PageIndex INT;
DECLARE #RowsPerPage INT;
SET #PageIndex = 0;
SET #RowsPerPage = 15;
SET #startRowIndex = (#PageIndex * #RowsPerPage) + 1;
WITH messageentries
AS (SELECT Row_number()
OVER(ORDER BY score DESC) AS row,
Count(DISTINCT town.townid) AS towns,
user_details.username,
user_score.score,
allience.alliencename,
allience.allienceid,
allience.alliencetagname,
(SELECT Count(* ) FROM user_details) AS numberofrows
FROM user_details
INNER JOIN user_score
ON user_details.username = user_score.username
INNER JOIN town
ON user_details.username = town.townownername
LEFT OUTER JOIN allience_roles
ON user_details.useralliencerole = allience_roles.roleid
LEFT OUTER JOIN allience
ON allience_roles.allienceid = allience.allienceid
GROUP BY user_details.username,
user_score.score,
allience.alliencename,
allience.allienceid,
allience.alliencetagname)
SELECT *, (SELECT row FROM messageentries WHERE username = 'myUsername') AS myself
FROM messageentries
WHERE row BETWEEN #startRowIndex AND #StartRowIndex + #RowsPerPage - 1
END
That works, but isn't the two nested selects going to run once for every row in the table? :/
...
(SELECT Count(* ) FROM user_details) AS numberofrows
...
(SELECT row FROM messageentries WHERE username = 'myUsername') AS myself
So my question being how can i get the values i want as "low-cost" as possible, and preferably in the same query?
Thanks in advance :)

try this...
DECLARE #NumberOfRows INT
SELECT #NumberOfRows = Count(* ) FROM user_details
WITH messageentries
AS (SELECT Row_number()
OVER(ORDER BY score DESC) AS row,
Count(DISTINCT town.townid) AS towns,
user_details.username,
user_score.score,
allience.alliencename,
allience.allienceid,
allience.alliencetagname,
#NumberOfRows AS numberofrows
FROM user_details
INNER JOIN user_score
ON user_details.username = user_score.username
INNER JOIN town
ON user_details.username = town.townownername
LEFT OUTER JOIN allience_roles
ON user_details.useralliencerole = allience_roles.roleid
LEFT OUTER JOIN allience
ON allience_roles.allienceid = allience.allienceid
GROUP BY user_details.username,
user_score.score,
allience.alliencename,
allience.allienceid,
allience.alliencetagname)
SELECT *, MyRowNumber.row AS myself
FROM messageentries,
(SELECT row FROM messageentries WHERE username = 'myUsername') MyRowNumber
WHERE row BETWEEN #startRowIndex AND #StartRowIndex + #RowsPerPage - 1

(SELECT Count(* ) FROM user_details)
This one will be cached (most probably materialized in a Worktable).
(SELECT row FROM messageentries WHERE username = 'myUsername')
For this one, most probably a Lazy Spool (or Eager Spool) will be built, which will be used to pull this value.

Related

How to use keyset pagination?

Suppose I have total 800 eligible rows in database which is ordered by a column requisitionid in descending order. I want to display the records in 80 pages each page having 10 rows. We are using requisitionid as seek predicate. So the predicate should be less than or greater than? As the query will progress from UI (Angular + primeNG), I want to send just one parameter - requisitionid. If it is less than query, then the query will be SELECT ... FROM ... where requisitionid < ?, so here we are talking about first row. If we go for greater than query i.e. SELECT ... FROM ... where requisitionid > ?, here we are talking about last row of the page.
Please refer to Life without offset
EDIT
Actual code:
with topten as (SELECT DISTINCT TOP 10
REQN.CASE_ID
,userContact2.BV_First_Name + ' ' + userContact2.BV_Last_Name ReQCreater
,REQN.BV_Internal_Job_Title
,REQN.BV_Posted_Job_Title as postedJobTitle
,REQN.BV_Status
,REQN.BV_Taleo_Id
,REQN.BV_WD_PositionID
,jobcode.BV_Job_Code
,loc.BV_LocationCode
,loc.BV_LocationName
,D.BV_Division_Code AS 'divCode',
ISNULL(loc.BV_Address1,'') + CASE WHEN ISNULL(loc.BV_Address1,'') = '' THEN '' ELSE ', ' END + ISNULL(loc.BV_Address2,'') + CASE WHEN ISNULL(loc.BV_Address2,'') = '' THEN '' ELSE ', ' END
+ ISNULL(loc.BV_City,'') + CASE WHEN ISNULL(loc.BV_City,'') = '' THEN '' ELSE ', ' END + ISNULL(loc.BV_State,'') + CASE WHEN ISNULL(loc.BV_State,'') = '' THEN '' ELSE (case when ISNULL(loc.BV_ZipCode,'') = '' THEN '' ELSE ', ' END) END
+ ISNULL(loc.BV_ZipCode,'') AS locationAddress
from dbo.CW_V_REQN as REQN
INNER JOIN dbo.CW_TL_Requisition__Location_Master as reqLocLink on REQN.CASE_ID = reqLocLink.FROM_ID
INNER JOIN dbo.CW_V_LOCTMAST as loc on loc.CASE_ID = reqLocLink.TO_ID
INNER JOIN dbo.CW_TL_UserContactInfo__Location_Master as locUserLink on locUserLink.TO_ID = loc.CASE_ID
INNER JOIN dbo.CW_V_USERCONT as userContact on userContact.CASE_ID = locUserLink.FROM_ID
LEFT JOIN dbo.CW_TL_Requisition__Department as reqDeptLink on REQN.CASE_ID = reqDeptLink.FROM_ID
INNER JOIN dbo.CW_V_DEPARTME as dept on dept.CASE_ID = reqDeptLink.TO_ID
LEFT JOIN dbo.CW_TL_UserContactInfo__Department_Master as deptUserLink on dept.CASE_ID=deptUserLink.TO_ID
INNER JOIN dbo.CW_TL_Requisition__Job_Code as reqJobLink on REQN.CASE_ID = reqJobLink.FROM_ID
LEFT JOIN dbo.CW_V_JOBCODE as jobcode on jobcode.CASE_ID = reqJobLink.TO_ID
LEFT JOIN dbo.CW_V_USERCONT as userContact2 on (userContact2.BV_Login_Name = REQN.CREATED_BY)
LEFT JOIN CW_TL_LocationMaster__Division_Master LD ON (LD.FROM_ID = loc.CASE_ID)
LEFT JOIN CW_V_DIVISION D ON (D.CASE_ID = LD.TO_ID)
WHERE userContact.BV_Login_Name = #LOGINNAME
AND REQN.CASE_ID < #MINCASEIDPREVPAGE
ORDER BY REQN.CASE_ID DESC
)
select topten.*
, T.*
from topten
cross join (select min(case_id) as min from topten) as T
For key based pagination on a descending key, the WHERE clause predicate should be < for the next page and > for the previous page. Also, the ORDER BY clause for the previous page needs to be ASC (for the TOP predicate) along an outer DESC (for the descending key display sequence). Below is an example.
--create test table with sample data
CREATE TABLE dbo.YourTable(
requisitionid int PRIMARY KEY
);
WITH
t10 AS (SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0)) t(n))
,t1000 AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0)) AS num FROM t10 AS a CROSS JOIN t10 AS b CROSS JOIN t10 AS c)
INSERT INTO dbo.YourTable WITH(TABLOCKX)
SELECT num
FROM t1000
WHERE num <= 800;
GO
--query for first page (page 1) on descending key (returns 800-791)
SELECT TOP(10) requisitionid
FROM YourTable
ORDER BY requisitionid DESC;
GO
--query for next page (page 2) on descending key (returns 790-781)
DECLARE #LastRequisitionIdOnPage int = 791;
SELECT TOP(10) requisitionid
FROM YourTable
WHERE requisitionid < #LastRequisitionIdOnPage
ORDER BY requisitionid DESC;
GO
--query for previous page (page 1) on descending key (returns 800-791)
DECLARE #FirstRequisitionIdOnPage int = 790;
SELECT requisitionid
FROM (
SELECT TOP(10) requisitionid
FROM YourTable
WHERE requisitionid > #FirstRequisitionIdOnPage
ORDER BY requisitionid ASC
) AS prev_page
ORDER BY requisitionid DESC;;
GO
If I understand your question, you should be able to use the follow clause at the end of your SELECT query:
OFFSET (#Page * 10) ROWS FETCH NEXT 10 ROWS ONLY
If this is not what you are looking for, please post your current query so we can see what you are doing now.

How to ignore duplicate records in CTE Select statement?

I am trying to ignore duplicate records in CTE but I am not able to do that, It seems like a SELECT statement inside CTE does not allow to use ROWNUM() variable numrows to condition in WHERE clause as it is showing Invalid column name 'numrows' error while trying to do so.
SQL Query:
DECLARE #BatchID uniqueidentifier = NEWID();
DECLARE #ClusterID SMALLINT = 1;
DECLARE #BatchSize integer = 20000;
DECLARE #myTableVariable TABLE(EventID BIGINT,HotelID int, BatchStatus varchar(50),BatchID uniqueidentifier);
WITH PendingExtResSvcEventsData_Batch
AS(
SELECT TOP (#BatchSize) t.EventID, t.HotelID, t.BatchStatus, t.BatchID, ROW_NUMBER() OVER (PARTITION BY t.EventID ORDER BY t.EventID) numrows
FROM ExtResSvcPendingMsg t WITH (NOLOCK)
WHERE t.ClusterID = #ClusterID AND numrows = 1 AND NOT EXISTS -- not allowed to use WHERE numrows = 1 here showing *Invalid Column Name*
(select 1 from ExtResSvcPendingMsg t2 where t2.BatchStatus = 'Batched'
and t2.EventID = t.EventID and t2.HotelID = t.HotelID)
)
UPDATE PendingExtResSvcEventsData_Batch
SET BatchStatus='Batched',
BatchID = #BatchID
-- WHERE numrows = 1 (not allowed to use WHERE here because of OUTPUT Clause)
OUTPUT INSERTED.* INTO #myTableVariable
SELECT e.ExtResSvcEventID,e.HotelID,e.ID1,e.ID2,e.ExtResSvcEventType,e.HostID,e.StatusCode,e.ChannelID,e.RequestAtTime,e.ProcessTime,e.DateBegin,e.DateEnd,
e.StatusMsg,em.MsgBodyOut,em.MsgBodyIn,e.ChannelResID
FROM ExtResSvcEvent e WITH (NOLOCK)
INNER JOIN #myTableVariable t ON e.ExtResSvcEventID = t.EventID
INNER JOIN ExtResSvcEventXML em with (nolock) on t.EventID = em.ExtResSvcEventID
ORDER BY e.ExtResSvcEventID
I have also tried to use numrows in final SELECT like INNER JOIN #myTableVariable t ON e.ExtResSvcEventID = t.EventID AND t.numrows = 1 but this gives me a error i.e. The column reference "inserted.numrows" is not allowed because it refers to a base table that is not being modified in this statement.
How do I ignore the duplicate records while using SELECT in CTE?
You can't refer to the numrows column in the WHERE clause of the CTE because that column is not calculated at this point in the plan execution. You need to add a second CTE with a select statement where you can refer to the numrows column:
WITH Base AS (
SELECT TOP (#BatchSize) t.EventID, t.HotelID, t.BatchStatus, t.BatchID, ROW_NUMBER() OVER (PARTITION BY t.EventID ORDER BY t.EventID) numrows
FROM ExtResSvcPendingMsg t WITH (NOLOCK)
WHERE t.ClusterID = #ClusterID
AND NOT EXISTS (select 1 from ExtResSvcPendingMsg t2 where t2.BatchStatus = 'Batched' and t2.EventID = t.EventID and t2.HotelID = t.HotelID)
), PendingExtResSvcEventsData_Batch AS (
SELECT EventID,
HotelID,
BatchStatus,
BatchID
WHERE numrows = 1
)
UPDATE...
I can't vouch for the update statement working as you expect it but the PendingExtResSvcEventsData_Batch should now have one row per EventID.

Counting records in a SQL subquery

I'm having difficult with a subquery. In plain English I'm trying to pick a random userID from the QCUsers table that has less than 20 records from the QCTier1_Assignments table. The problem is that my query below is only picking users where it meets the criteria of the inner query when I need it to pick any user from QCUsers table even if the user does not have any records at all in the QCTier1_Assignments table. I need something like this
AND (Sub.QCCount < 20 OR Sub.QCCount = 0 )
DECLARE #ReviewPeriodMonth varchar(10) = '10'
DECLARE #ReviewPeriodYear varchar(10) = '2015'
SELECT TOP 1
E1.UserID
,Sub.QCCount --Drawn from the subquery
FROM QCUsers E1
JOIN (SELECT
QCA.UserID,
COUNT(*) AS QCCount
FROM QCTier1_Assignments QCA
WHERE QCA.ReviewPeriodMonth = #ReviewPeriodMonth
AND QCA.ReviewPeriodYear = #ReviewPeriodYear
GROUP BY QCA.UserID
) Sub
ON E1.UserID = Sub.UserID
WHERE Active = 1
AND Grade = 12
AND Sub.QCCount < 20
ORDER BY NEWID()
I also tried it this way with no luck
DECLARE #ReviewPeriodMonth varchar(10) = '10'
DECLARE #ReviewPeriodYear varchar(10) = '2015'
SELECT TOP 1
E1.UserID
,Sub.QCCount --Drawn from the subquery
FROM QCUsers E1
RIGHT JOIN (SELECT
QCA.UserID,
ReviewPeriodMonth,
ReviewPeriodYear,
COUNT(*) AS QCCount
FROM QCTier1_Assignments QCA
GROUP BY
QCA.UserID,
ReviewPeriodMonth,
ReviewPeriodYear
) Sub
ON E1.UserID = Sub.UserID
WHERE Active = 1
AND Grade = 12
AND Sub.QCCount < 20
AND Sub.ReviewPeriodMonth = #ReviewPeriodMonth
AND Sub.ReviewPeriodYear = #ReviewPeriodYear
ORDER BY NEWID()
Try using your second query but change the WHERE clause to use COALESCE(Sub.QCCount, 0) instead of justSub.QCCount`
If the subquery returns no rows then with your RIGHT JOIN you'll at least still get the row, but the QCCount will be NULL which when compared to anything will result in a "false" effectively.
Also, you should look into the HAVING clause. It might allow you to do this without a subquery at all.
Here's an example with the HAVING clause. If it doesn't give the correct results please let me know as I'm not able to test this.
DECLARE
#ReviewPeriodMonth VARCHAR(10) = '10'
#ReviewPeriodYear VARCHAR(10) = '2015'
SELECT TOP 1
E1.UserID,
COUNT(QCA.UserID) AS QCCount
FROM
QCUsers E1
LEFT OUTER JOIN QCTier1_Assignments QCA ON
QCA.UserID = E1.UserID AND
QCA.ReviewPeriodMonth = #ReviewPeriodMonth AND
QCA.ReviewPeriodYear = #ReviewPeriodYear
WHERE
E1.Active = 1 AND
Grade = 12 AND
HAVING
COUNT(*) < 20
ORDER BY
NEWID()
You should use LEFT JOIN instead of JOIN(INNER JOIN), And you'd better to put the predicate to the outer query based on your practice, but I recommend the following way:
SELECT TOP1 ABC.UserID,ABC.QCCount
FROM
(
SELECT E1.UserID, COUNT(*) as QCCount
FROM QCUsers as E1
LEFT JOIN QCTier1_Assignments as QCA
ON QCA.UserID = E1.UserID
WHERE QCA.ReviewPeriodMonth = #ReviewPeriodMonth
AND QCA.ReviewPeriodYear = #ReviewPeriodYear
AND Active = 1
AND Grade = 12
GROUP BY E1.UserID
) as ABC
WHERE ABC.QCCount <20
ORDER BY NEWID()
I was able to work it out through a combination of responses here
DECLARE #ReviewPeriodMonth varchar(10) = '10'
DECLARE #ReviewPeriodYear varchar(10) = '2015'
SELECT TOP 1
QCUsers.UserID,
COUNT(QCTier1_Assignments.ReviewID) AS ReviewCount
FROM
QCTier1_Assignments RIGHT OUTER JOIN
QCUsers ON QCTier1_Assignments.UserID = QCUsers.UserID
WHERE
QCUsers.Active = 1
AND QCUsers.Grade = '12'
AND (ReviewPeriodMonth = #ReviewPeriodMonth OR ReviewPeriodMonth IS NULL)
AND (ReviewPeriodYear = #ReviewPeriodYear OR ReviewPeriodYear IS NULL)
GROUP BY
QCUsers.UserID
HAVING
(COALESCE(COUNT(QCTier1_Assignments.ReviewID),0) < 4)
ORDER BY NEWID()

How to select minimum non duplicated value in a column?

Can you help me with SQL statements to find minimum non duplicated value?
This is my sql statement
DECLARE #currentDate DATETIME = CONVERT(VARCHAR(10), Getdate(), 120)
UPDATE Dinfo
SET WinnerID = result.CustomerID
FROM Daily_Info Dinfo
JOIN (SELECT CO.DailyInfoID,
CO.CustomerID
FROM Customer_Offer CO
WHERE CO.OfferDate = #currentDate
GROUP BY CO.DailyInfoID,
CO.CustomerID
HAVING ( Count(CO.OfferPrice) = 1 )) result
ON Dinfo.DailyID = result.DailyInfoID
and i want to update my winner who offered minimum unique offer. How can i select it?
If you want to find data, then I would expect a select. I think the following query might do what you want:
select min(offerprice)
from (select co.*, count(*) over (partition by co.offerprice) as cnt
from Customer_Offer co
where CO.OfferDate = #currentDate
) co
where cnt = 1;
If you want to update information based on this, then use join:
update dinfo
set winnerId = c.CustomerId
from dinfo cross join
(select top 1 co.*
from (select co.*, count(*) over (partition by co.offerprice) as cnt
from Customer_Offer co
where CO.OfferDate = #currentDate
) co
where cnt = 1
order by offerprice
) c
This follows the structure of your query, but it is going to update all rows in dinfo. You might want some other conditions to so only one row is updated.

MSSQL Paging is returning random rows when not supposed too

I'm trying to do some basic paging in MSSQL. The problem I'm having is that I'm sorting the paging on a row that (potentially) has similar values, and the ORDER BY clause is returning "random" results, which doesn't work well.
So for example.
If I have three rows, and I'm sorting them by a "rating", and all of the ratings are = '5' - the rows will seemingly "randomly" order themselves. How do I make it so the rows are showing up in the same order everytime?
I tried ordering it by a datetime that the field was last edited, but the "rating" is sorted in reverse, and again, does not work how i expect it to work.
Here is the SQL I'm using thus far. I know it's sort of confusing without the data so.. any help would be greatful.
SELECT * FROM
(
SELECT
CAST(grg.defaultthumbid AS VARCHAR) + '_' +
CAST(grg.garageid AS VARCHAR) AS imagename,
(
SELECT COUNT(imageid)
FROM dbo.images im (nolock)
WHERE im.garageid = grg.garageid
) AS piccount,
(
SELECT COUNT(commentid)
FROM dbo.comments cmt (nolock)
WHERE cmt.garageid = grg.garageid
) AS commentcount,
grg.GarageID, mk.make, mdl.model, grg.year,
typ.type, usr.username, grg.content,
grg.rating, grg.DateEdit as DateEdit,
ROW_NUMBER() OVER (ORDER BY Rating DESC) As RowIndex
FROM
dbo.garage grg (nolock)
LEFT JOIN dbo.users (nolock) AS usr ON (grg.userid = usr.userid)
LEFT JOIN dbo.make (nolock) AS mk ON (grg.makeid = mk.makeid)
LEFT JOIN dbo.type (nolock) AS typ ON (typ.typeid = mk.typeid)
LEFT JOIN dbo.model (nolock) AS mdl ON (grg.modelid = mdl.modelid)
WHERE
typ.type = 'Automobile' AND
grg.defaultthumbid != 0 AND
usr.username IS NOT NULL
) As QueryResults
WHERE
RowIndex BETWEEN (2 - 1) * 25 + 2 AND 2 * 25
ORDER BY
DateEdit DESC
Try ordering by both, e.g.:
ORDER BY Rating DESC, DateEdit ASC
The query first numbers the rows by [Rating], and then re-sorts the results by [DateEdit]. Possibly not what you intended. Ordering by [RowIndex] ASC should sort it out.
ROW_NUMBER() OVER (ORDER BY [Rating] DESC) As [RowIndex]
...
ORDER BY [RowIndex]