SQL Server run SELECT for each in list - sql

I won't be surprised if SQL just doesn't work this way at all, but:
If we run two SELECT statements in a query, we get a split "Results" pane. I'm wondering if I can add variables to a list, and then have the number of result pane splits match the length of that list.
If I were to mix languages:
id_list = [26275, 54374, 84567]
for i in id_list:
SELECT * FROM table WHERE id = i
I'm just trying to easily compare results of a query while keeping distinct groups, with a changing number of variables. Since loops never seem to be the answer in SQL, I'd be just as happy inserting something like a blank line or horizontal rule, etc. Not sure if that's possible either though...

There is no concept of "lists" (as a separate data structure) in T-SQL. Does this do what you want?
SELECT *
FROM table
WHERE id IN (26275, 54374, 84567);

declare #i int = 0;
declare #Id int;
declare #Ids table (Id int);
insert #Ids select Id from (values (26275), (54374), (84567)) t(Id);
-- OR: insert #Ids select * from string_split('26275, 54374, 84567', ',');
declare #Count int = (select count(*) from #Ids);
while #i < #Count
begin
select #Id = Id, #i = #i + 1
from #Ids order by Id
offset #i rows fetch next 1 rows only;
select * from dbo.MyTable where Id = #Id;
end

You can use UNION ALL:
SELECT * FROM table WHERE id = 26275
UNION ALL
SELECT * FROM table WHERE id = 54374
UNION ALL
SELECT * FROM table WHERE id = 84567

Related

SQL - Variable assignment with other columns selection

I am trying to get the records as well as records count using common table expressions, but I want to store the records count in a variable and it will ofcourse give me the error
A SELECT statement that assigns a value to a variable must not be
combined with data-retrieval operations.
I am trying something like this
declare #count int
;with allRecords as
(
-- query fetching all the records with many joins
),
recordsCount as
(
select count(*) as Total from allRecords
)
select allRecords.*, #count=recordsCount.Total from allRecords, recordsCount
where -- multiple conditions
Is there any work around for this?
Actually the #count variable is an output variable of my stored procedure so I want to return the result as well as fill this #count variable
You can't do it like this. If you want to get the number of rows the select statement returned into a variable you should use the built-in global variable ##ROWCOUNT:
DECLARE #count int
;WITH allRecords as
(
-- query fetching all the records with many joins
)
SELECT allRecords.*
FROM allRecords
SELECT #Count = ##ROWCOUNT
Update:
Well, in that case you have no choise that I'm aware of other then using a temporary table:
SELECT /* columns */ INTO #tempTableName
-- rest of the select statement
SELECT #Count = COUNT(*)
FROM #tempTableName
SELECT *
FROM #tempTableName
WHERE <conditions>
DROP TABLE #tempTableName
declare #count int
;with allRecords as
(
-- query fetching all the records with many joins
)
select #count = count(*) as Total from allRecords
I'd use a temp table or table variable here. You can then do separate statements for select #count = count(*) from #allrecords and select * from #allrecords

Find a column where the identity column is breaking

I have a table, e.g.
cust_ord_key
1
2
3
4
5
7
9
How do I write a query to find out if the numbers are in sequence and not breaking anywhere?
In SQL Server:
SELECT SeqID AS MissingSeqID
FROM (SELECT ROW_NUMBER() OVER (ORDER BY column_id) SeqID from sys.columns) LkUp
LEFT JOIN dbo.TestData t ON t.ID = LkUp.SeqID
You may do something like:
DECLARE #RESULT int;
SET #RESULT = 0;
DECLARE #FIRST_ID int;
DECLARE #LAST_ID int;
DECLARE #THIS_VALUE int;
DECLARE #NEXT_VALUE int;
SELECT #FIRST_ID = min(ID), #LAST_ID = max(ID) from table_name;
WHILE(#FIRST_ID <= #LAST_ID)
BEGIN
SELECT #THIS_VALUE = your_field_with_keys from table_name where ID = #FIRST_ID;
SELECT #NEXT_VALUE = your_field_with_keys from table_name where ID = (#FIRST_ID + 1);
if #THIS_VALUE > #NEXT_VALUE
SET #RESULT = #FIRST_ID;
--break your query here or do anything else
SET #FIRST_ID = #FIRST_ID + 1;
END
What does this query do? We declare #RESULT variable for taking an ID of key, where you key is breaking. #FIRST_ID and #LAST_ID are the minimal and maximal IDs from your table, we will use them later. #THIS_VALUE and #NEXT_VALUE are two variables for two keys to be compared.
Then we execute loop over our IDs. Then setting up #THIS_VALUE and #NEXT_VALUE with corresponding keys (this and the next). If #THIS_VALUE more than #NEXT_VALUE, it means that the key is breaking here (if previous key is more than next key), and we take the ID of element, where key is broken. And there you may stop your query or do some required logic.
This is not perfect, but definitely does the job, and is universal across all DB engines.
SELECT t1.id FROM myTable t1
LEFT JOIN myTable t2 ON t1.id+1 = t2.id
WHERE t2.id IS NULL
When the identity doesn't break anywhere, this will only return the last entry. You can compensate for that with a procedural language, by first getting the MAX(ID) adding that to the WHERE clause like this:
WHERE t2.id IS NULL AND t1.id<>5643
where 5643 is the max id (either a variable introduced in the query string, or can be a variable in the procedural SQL language of whatever DB engine you're using). The point is that it's the maximum value of the identity on that table.
OR, you can just dismiss the last row from the result set if you're doing it in PHP or whatever.

Finding Uppercase Character then Adding Space

I bought a SQL World City/State database. In the state database it has the state names pushed together. Example: "NorthCarolina", or "SouthCarolina"...
IS there a way in SQL to loop and find the uppercase characters and add a space???
this way "NorthCarolina" becomes "North Carolina"???
Create this function
if object_id('dbo.SpaceBeforeCaps') is not null
drop function dbo.SpaceBeforeCaps
GO
create function dbo.SpaceBeforeCaps(#s varchar(100)) returns varchar(100)
as
begin
declare #return varchar(100);
set #return = left(#s,1);
declare #i int;
set #i = 2;
while #i <= len(#s)
begin
if ASCII(substring(#s,#i,1)) between ASCII('A') and ASCII('Z')
set #return = #return + ' ' + substring(#s,#i,1)
else
set #return = #return + substring(#s,#i,1)
set #i = #i + 1;
end;
return #return;
end;
GO
Then you can use it to update your database
update tbl set statename = select dbo.SpaceBeforeCaps(statename);
There's a couple ways to approach this
Construct a function using a pattern and the PATINDEX feature.
Chain minimal REPLACE statements for each case (e.g. REPLACE(state_name, 'hC', 'h C' for your example case). This seems is kind of a hack, but might actually give you the best performance, since you have such a small set of replacements.
If you absolutely cannot create functions and need this as a one-off, you can use a recursive CTE to break the string up (and add the space at the same time where required), then recombine the characters using FOR XML. Elaborate example below:
-- some sample data
create table #tmp (id int identity primary key, statename varchar(100));
insert #tmp select 'NorthCarolina';
insert #tmp select 'SouthCarolina';
insert #tmp select 'NewSouthWales';
-- the complex query updating the "statename" column in the "#tmp" table
;with cte(id,seq,char,rest) as (
select id,1,cast(left(statename,1) as varchar(2)), stuff(statename,1,1,'')
from #tmp
union all
select id,seq+1,case when ascii(left(rest,1)) between ascii('A') and ascii('Z')
then ' ' else '' end + left(rest,1)
, stuff(rest,1,1,'')
from cte
where rest > ''
), recombined as (
select a.id, (select b.char+''
from cte b
where a.id = b.id
order by b.seq
for xml path, type).value('/','varchar(100)') fixed
from cte a
group by a.id
)
update t
set statename = c.fixed
from #tmp t
join recombined c on c.id = t.id
where statename != c.fixed;
-- check the result
select * from #tmp
----------- -----------
id statename
----------- -----------
1 North Carolina
2 South Carolina
3 New South Wales

top count for a SQL query

I want to have a variable for selecting top rows. I can select top rows based on a variable. However I want to select all rows if the variable is not supplied.
Currently I'm using this query:
DECLARE #TOPCOUNT int;
SET #TOPCOUNT=10;
SELECT TOP(#TOPCOUNT) * FROM TABLE1
Update:
The original query is very lengthy and complex, so I don't to rewrite the entire query without top count in else clause.
I don't want to use dynamic query because of its repercussions.
Something like this:
DECLARE #TOPCOUNT int;
--SET #TOPCOUNT=10;
IF #TOPCOUNT IS NULL
SELECT * FROM TABLE1
ELSE
SELECT TOP(#TOPCOUNT) * FROM TABLE1
Added after above UPDATE - if this is a parameter of a Stored Procedure then just provide a default for #TOPCOUNT:
#TOPCOUNT INT = 2147483647 --max size of INT
Something like this will help. Just init your #TOPCOUNT with -1 if you want all rows.
IF #TOPCOUNT = -1 BEGIN
SELECT * FROM TABLE1
END
ELSE BEGIN
SELECT TOP(#TOPCOUNT) * FROM TABLE1
END
IF #TOPCOUNT IS NULL SET #TOPCOUNT=2147483647

Split query result by half in TSQL (obtain 2 resultsets/tables)

I have a query that returns a large number of heavy rows.
When I transform this rows in a list of CustomObject I have a big memory peak, and this transformation is made by a custom dotnet framework that I can't modify.
I need to retrieve a less number of rows to do "the transform" in two passes and then avoid the memory peak.
How can I split the result of a query by half? I need to do it in DB layer. I thing to do a "Top count(*)/2" but how to get the other half?
Thank you!
If you have identity field in the table, select first even ids, then odd ones.
select * from Table where Id % 2 = 0
select * from Table where Id % 2 = 1
You should have roughly 50% rows in each set.
Here is another way to do it from(http://www.tek-tips.com/viewthread.cfm?qid=1280248&page=5). I think it's more efficient:
Declare #Rows Int
Declare #TopRows Int
Declare #BottomRows Int
Select #Rows = Count(*) From TableName
If #Rows % 2 = 1
Begin
Set #TopRows = #Rows / 2
Set #BottomRows = #TopRows + 1
End
Else
Begin
Set #TopRows = #Rows / 2
Set #BottomRows = #TopRows
End
Set RowCount #TopRows
Select * From TableName Order By DisplayOrder
Set RowCount #BottomRows
Select * From TableNameOrder By DisplayOrderDESC
--- old answer below ---
Is this a stored procedure call or dynamic sql? Can you use temp tables?
if so, something like this would work
select row_number() OVER(order by yourorderfield) as rowNumber, *
INTO #tmp
FROM dbo.yourtable
declare #rowCount int
SELECT #rowCount = count(1) from #tmp
SELECT * from #tmp where rowNumber <= #rowCount / 2
SELECT * from #tmp where rowNumber > #rowCount / 2
DROP TABLE #tmp
SELECT TOP 50 PERCENT WITH TIES ... ORDER BY SomeThing
then
SELECT TOP 50 PERCENT ... ORDER BY SomeThing DESC
However, unless you snapshot the data first, a row in the middle may slip through or be processed twice
I don't think you should do that in SQL, unless you will always have a possibility to have the same record 2 times.
I would do it in an "software" programming language, not SQL. Java, .NET, C++, etc...