How I prevent the bottom Order By from effecting everything in #Result? - sql

I need to prevent the Order By SettingID at the bottom of this SQL from re-ordering everything. I want to Order my first select by UserID and my second select by OrderID. How do put a bound on the bottom Order By? I tried parentheses but I'm a T-SQL beginner so couldn't get that figured out. I tried using #ResultA and #ResultB and then Select them into #Result but the end result is the same the bottom Order By applies to the entire #Result. Would temp tables be the way to go and if so how?
DECLARE #Result TABLE (SettingID INT, GroupID INT, UserID INT)
INSERT INTO #Result
SELECT
SettingID,
GroupID,
UserID
FROM Table1
WHERE (GroupID = #GroupID AND UserID = #UserID)
ORDER BY UserID
INSERT INTO #Result
SELECT
SettingID,
GroupID,
UserID
FROM Tabel1
WHERE (GroupID IS NULL)
ORDER BY SettingID DESC -- This Order By is reordering everything in #Result.
SELECT * FROM #Result

You should not depend on the ordering of rows in a table, even table variables. In SQL, tables represent unordered sets. You can do what with a single query:
SELECT SettingID, GroupID, UserID
FROM Table1
ORDER BY (CASE WHEN (GroupID = #GroupID AND UserID = #UserID) THEN 1 ELSE 0 END) DESC,
GroupID
You can put this into a table variable:
INSERT INTO #Result
SELECT SettingID, GroupID, UserID
FROM Table1
ORDER BY (CASE WHEN (GroupID = #GroupID AND UserID = #UserID) THEN 1 ELSE 0 END) DESC,
GroupID;
However, you cannot assume that:
SELECT *
FROM #Result
will be in any particular order, such as insert order.
There is a trick if you want to do that. Create an identity column in the temporary table and order by that:
DECLARE #Result TABLE (
ResultId INT IDENTITY(1, 1) PRIMARY KEY,
SettingID INT,
GroupID INT,
UserID INT
);
INSERT INTO #Result(SettingID, GroupID, UserID)
SELECT SettingID, GroupID, UserID
FROM Table1
ORDER BY (CASE WHEN (GroupID = #GroupID AND UserID = #UserID) THEN 1 ELSE 0 END) DESC,
GroupID;
SELECT *
FROM #Result
ORDER BY ResultId;

Use commas to add ORDER BY layers:
SELECT * FROM #Result ORDER BY UserID, OrderID
You can and should remove the order by in the queries that populate your table variable.

Tables don't have a concept of record order as you perceive it. It just doesn't exist.
If you need to guarantee that results are returned in a specific order, you must use an ORDER BY on the select query that produces those results. With that in mind, you should probably just omit the ORDER BY clause on your INSERT statements, and only include it on the final select.
More than that, if controlling the order is the whole reason for the temp table, you should really just do this:
SELECT
SettingID,
GroupID,
UserID
FROM Table1
WHERE (GroupID = #GroupID AND UserID = #UserID) OR #GroupID IS NULL
ORDER BY CASE WHEN GroupID IS NOT NULL THEN 0 ELSE 1 END
, CASE WHEN GroupID IS NOT NULL THEN UserID ELSE SettingID END

ORDER BY
CASE WHEN (SettingID between x and y)
THEN UserID
else OrderID END

Related

How do I limit the number of ROWS being returned in my SQL Server Function?

CREATE FUNCTION [dbo].[GET_CUSTOMER_DATA]
(
-- Add the parameters for the function here
#customerID bigint,
#maxRows int,
#offset int,
#rows int
)
RETURNS TABLE
AS
RETURN
(
-- Add the SELECT statement with parameter references here
SELECT *
FROM SS_CustomerCard AS SS_CC
INNER JOIN SS_PersonalRepresentative AS SS_PR
ON SS_PR.customerID = SS_CC.ID
INNER JOIN SS_ApplicationStatus AS SS_AS
ON SS_AS.CustomerID = SS_CC.ID
WHERE
SS_CC.ID='#customerID'
ORDER BY SS_AS.EventDateTime DESC, SS_CC.FirstName DESC, SS_CC.LastName DESC
OFFSET #offset ROWS
FETCH NEXT #rows ROWS ONLY
)
GO
I want to pass a parameter to limit the number of rows being returned (maxRows)
You can use the TOP() clause in the SELECT statement.
Here is a conceptual example.
SQL
DECLARE #tbl TABLE (ID INT IDENTITY PRIMARY KEY, vehicleMake VARCHAR(20));
INSERT INTO #tbl (vehicleMake) VALUES
('Chevrolet'),
('Tesla'),
('Audi'),
('Nissan');
DECLARE #tableVariable TABLE (ID INT, vehicleMake VARCHAR(20));
DECLARE #topRows INT = 2;
INSERT INTO #tableVariable
SELECT *
FROM #tbl
ORDER BY id;
SELECT TOP(#topRows) *
FROM #tableVariable
ORDER BY id;
I think you are trying to limit the total number of rows BEFORE the OFFSET/FETCH. If so, you need to put it in a subquery/derived table, and add another ORDER BY:
CREATE FUNCTION [dbo].[GET_CUSTOMER_DATA]
(
-- Add the parameters for the function here
#customerID bigint,
#maxRows int,
#offset int,
#rows int
)
RETURNS TABLE
AS
RETURN
(
SELECT *
FROM (
SELECT TOP (#maxRows) all_needed_columns_here_aliased_if_necessary
FROM SS_CustomerCard AS SS_CC
INNER JOIN SS_PersonalRepresentative AS SS_PR
ON SS_PR.customerID = SS_CC.ID
INNER JOIN SS_ApplicationStatus AS SS_AS
ON SS_AS.CustomerID = SS_CC.ID
WHERE
SS_CC.ID=#customerID
ORDER BY SS_AS.EventDateTime DESC, SS_CC.FirstName DESC, SS_CC.LastName DESC
) t
ORDER BY EventDateTime DESC, FirstName DESC, LastName DESC
OFFSET #offset ROWS
FETCH NEXT #rows ROWS ONLY
)
GO

How to get the each record with some condition

I have following data:
DECLARE #temp TABLE (
ID int
,sn varchar(200)
,comment varchar(2000)
,rownumber int
)
insert into #temp values(1,'sn1',NULL,1)
insert into #temp values(2,'sn1','aaa',2)
insert into #temp values(3,'sn1','bbb',3)
insert into #temp values(4,'sn1',NULL,4)
insert into #temp values(5,'sn2',NULL,1)
insert into #temp values(6,'sn2',NULL,2)
insert into #temp values(7,'sn2',NULL,3)
select * from #temp
And I want to output like this:
2 sn1 aaa 2
5 sn2 NULL 1
same sn, if comment have value, get this lower rownumber's record. For sn1, have two records with comment value, so here, get the the record with rownumber=2
If comment doesn't have value, get the lower rownumber's record. For sn2, get the record with rownumber=1
May I know how to write this SQL?
This is a prioritization query. I think row_number() is the simplest method:
select t.*
from (select t.*,
row_number() over (partition by sn
order by (case when comment is not null then 1 else 2 end),
rownumber
) as seqnum
from #temp t
) t
where seqnum = 1;
Here is a db<>fiddle.

INSERT INTO from SELECT: The select list for the INSERT statement contains more items than the insert list

I am still getting a weird error:
The select list for the INSERT statement contains more items than the insert list. The number of SELECT values must match the number of INSERT columns.
Code:
INSERT INTO #tab (Phone)
select t2.Phone
from
(
SELECT DISTINCT top 999 t3.Phone, MIN(t3.Ord)
FROM
(
select Phone1 as Phone, Ord from #tabTemp
union all
select Phone2 as Phone, Ord from #tabTemp
) t3
GROUP BY t3.Phone
ORDER BY MIN(t3.Ord) asc, t3.Phone
) t2
The idea is to select all phone numbers from #tabTemp with their row order. Then I wanna distinct them and insert distincted numbers into table #tab. Top 999 is here only for order by purpose, because I use it into a function (UDF).
Structures are following:
declare #tabTemp TABLE
(
Phone1 varchar(128) NULL,
Phone2 varchar(128) NULL,
Ord int
);
declate #tab TABLE
(
Phone varchar(max) NULL
);
EDITED:
FULL CODE
CREATE FUNCTION dbo.myFnc(#PID int, #VID int, #JID int, #ColumnNo int)
RETURNS #tab TABLE
(
Phone varchar(max) NULL
)
AS
BEGIN
if #PID is null and #VID is null and #JID is null
return;
if #ColumnNo is null or (#ColumnNo<>2 and #ColumnNo<>3 and #ColumnNo<>6)
return;
declare #catH int;
set #catH = dbo.fncGetCategoryID('H','tt'); -- just returning int value
declare #kvalP int;
set #kvalP = dbo.fncGetCategoryID('P','te');
declare #kvalR int;
set #kvalR = dbo.fncGetCategoryID('R','te');
declare #tabTemp TABLE
(
Phone1 varchar(128) NULL,
Phone2 varchar(128) NULL,
Ord int
);
-- finding parent subject + current one
WITH subj AS(
SELECT *
FROM Subjekt
WHERE
(ID = #PID and #PID is not null)
or
(ID = #VID and #VID is not null)
or
(ID = #JID and #JID is not null)
UNION ALL
SELECT t.*
FROM Subjekt t
INNER JOIN subj r ON r.ID = t.ID
)
INSERT INTO #tabTemp (Phone1,Phone2)
(select
(case when o.TYP1=#catH then o.TEL1 else null end) Phone1
,(case when o.TYP2=#catH then o.TEL2 else null end) Phone2
,so.POR_C
from
subj s
,SubjektPerson so
,Persons o
,recSetup idS
,recSetup idSO
,recSetup idO
where 1=1
and idO.isValid=1
and idSO.isValid=1
and idS.isValid=1
and idSO.ID0=so.ID
and idS.ID0=s.ID
and idO.ID0=o.ID
and so.ID_PERSON=o.ID
and so.ID_SUBJECT=s.ID
and (o.TYP=#kvalP or o.TYP=#kvalR)
)
INSERT INTO #tab (Phone)
select t2.Phone
from
(
SELECT DISTINCT top 999 t3.Phone, MIN(t3.Ord)
FROM
(
select Phone1 as Phone, Ord from #tabTemp
union all
select Phone2 as Phone, Ord from #tabTemp
) t3
GROUP BY t3.Phone
ORDER BY MIN(t3.Ord) asc, t3.Phone
) t2
RETURN
END
Not sure why you have distinct AND a group by on the same query. You could greatly simplify this.
INSERT INTO #tab (Phone)
SELECT top 999 t3.Phone
FROM
(
select Phone1 as Phone, Ord from #tabTemp
union all
select Phone2 as Phone, Ord from #tabTemp
) t3
GROUP BY t3.Phone
ORDER BY MIN(t3.Ord) asc, t3.Phone
Now for the error message you were receiving, it doesn't seem like it came from this block of code because the syntax is fine and the number of columns matches correctly. I suspect the error is somewhere earlier in your code.
Also, you might want to consider using temp tables instead of table variables since it seems like you have a lot of rows in these tables.
You've focussed on the wrong insert. This is the one with the mismatch:
INSERT INTO #tabTemp (Phone1,Phone2)
(select
(case when o.TYP1=#catH then o.TEL1 else null end) Phone1
,(case when o.TYP2=#catH then o.TEL2 else null end) Phone2
,so.POR_C
from
...
Two columns in the insert list, 3 columns in the subselect. I can't tell just from the naming whether POR_C was meant to end up in the Ord column or not.
On the surface, it appears you are maybe triggering a query planner bug or something. There are a number of iffy things going on:
The union all of the same table to itself
Using both group by and distinct
I'm not sure what you mean by
Top 999 is here only for order by purpose, because I use it into a function (UDF).
Do you mean this whole query is executed within a UDF? If so, are there other queries that might be giving that error?

optimizing SQL query with multiple keys

I have a table in a database with a primary key, and a 'second' key as well. This second key can have the same value occur more than once in the table, but often i only want to return the most recent row for that second key. I have an existing query that works below, but I feel like it's very ugly and there should be a simpler way to do this instead of creating a table variable, going through a loop, and inserting 1 row into the table variable on each pass through the loop. Am i making this too hard?
declare #RowCnt int
declare #MaxRows int
declare #secondID as uniqueidentifier
DECLARE #retList TABLE(
firstGUID uniqueidentifier,
secondGUID uniqueidentifier,
name nvarchar(50),
DateCreated datetime
)
select #RowCnt = 1
declare #Import table (rownum int IDENTITY (1, 1) Primary key NOT NULL , secondGUID uniqueidentifier)
insert into #Import (secondGUID) SELECT DISTINCT dbo.TestTable.secondGUID FROM dbo.TestTable
select #MaxRows=count(*) from #Import
while #RowCnt <= #MaxRows
begin
select #secondID=secondGUID from #Import where rownum = #RowCnt
INSERT INTO #retList
SELECT TOP (1) firstGUID,secondGUID,name,datecreated
FROM dbo.TestTable
WHERE dbo.TestTable.secondGUID = #secondID
ORDER BY DateCreated Desc
Set #RowCnt = #RowCnt + 1
END
select * from #retList
EDIT:
for example, imagine the table has these values
firstGUID secondGUID Name DateCreated
EAD50999-E9B1-43F0-9FA6-615405FA5A9A 6163B6ED-6AF4-494E-ACE6-184F4804847B Test1 2014-04-11 15:12:36.303
A9645486-1021-4E98-92AC-1205CC3FB9D3 6163B6ED-6AF4-494E-ACE6-184F4804847B Test2 2014-04-10 15:21:46.087
DEE375BB-BFAF-44BE-AC64-06D7702E2ACB 3BD0A2F0-4E44-43B9-BD24-003B518609C7 Test3
2014-04-11 15:22:37.097
I only want the Test1 and Test3 rows to be returned.
You could use SQLServer's analytical functions:
select firstGUID, secondGUID, name, datecreated
from (select t.*,
rank() over (partition by secondGUID order by datecreated desc) r
from TestTable t) ilv
where r=1
I'm not 100% sure I understand what you're asking, but it sounds like you want to select only the rows containing the max DateCreated. The normal way of doing that is to join with a subselect that uses a group by clause, eg.:
select tt.*
from TestTable tt
join (
select firstguid, max(DateCreated) as maxdate
from TestTable
group by firstguid
) gtmp on tt.firstguid = gtmp.firstguid and tt.dateCreated = gtmp.maxdate

How can I order data and add a record to the first position of the data set?

I know I can create a temp table, insert records, order it and then use union afterwards, but I'm looking for alternative routes. I tried a cte, but I had to order the entire thing which doesn't work as my unioned record doesn't stay "on top".
Basically, I have at able with Id INT, Name VARCHAR(MAX) fields and I want to ORDER BY Name before I add an entry at the row[0] position in the return set. If I order after the union, the row I wanted at row[0] gets ordered with it.
Any ideas?
You were on the right track with a union query. Force the sort with static values.
select 0 sortfield, '' name, etc
union
select 1 sortfield, name, etc
from etc
order by sortfield, name.
CREATE TABLE #temp (
idnt INT IDENTITY(2) NOT NULL --This begins the identity col with a value of 2
,Id INT
,Name VARCHAR(MAX)
)
INSERT INTO #temp
SELECT
...
FROM myTable
ORDER BY Name
CREATE TABLE #tempAPPEND (
idnt INT IDENTITY(1) NOT NULL --This begins the identity col with a value of 1
,Id INT
,Name VARCHAR(MAX)
)
INSERT INTO #tempAPPEND (Id, Name)
VALUES ('34384','Pinal Dave') -- SAMPLE VALUES
SELECT * FROM #temp
UNION
SELECT * FROM #tempAPPEND
ORDER BY idnt