How to generate 5 digit increment number based on Zone? - sql

My Out put like This : If it is North "N00001
N00002
If it is South "S00001
S00002
Here is my Tried Code
Declare #Zone varchar(20),#ZoneID int,#id varchar(10)
Set #Zone = 'S'
Select #ZoneID = cast(isnull(max(cast(replace(Idno,#Zone,'') as numeric))+1,'00000') as varchar) from memberprofiles Where left(idno,1) = #Zone
Set #id = #Zone+cast(#ZoneID as varchar)
select #id
But Every Time I am Getting "S1" But I need "S00001"
How can i generate Zone wise Number generation

This question is a "closed as duplicate" candidate. But, as there are several flaws, I think it's worth an answer:
In your database you seem to have a column "Idno" with a leading character marking the zone. If this is true, you should - if ever possible - change the design. The number and the zone mark should reside in two columns. Any combination of them is a presentation issue
Your LEFT(Idno,1) will perform badly (read about "sargability"). If there's an index on Idno, you should be better off with Idno LIKE ' + #Zone + '%'
Are you sure, that in "memberprofiles" there's only one row where your WHERE clause is true? If not, which number would you expect in "#ZoneID" after your SELECT?
Your cast(isnull(max(cast(replace(Idno,#Zone,'') as numeric))+1,'00000') ... replaces the leading "S" with nothing, hoping there is a number left. You'll get the highest number (OK, this answers point 3, but is still very - uhm - hacky), still you expect a "NULL" where you'd return a "00000". This cries for a better design loudly :-)
You should try to get into "set based" thinking, rather then "procedural" thinking...
Try this
CREATE TABLE #memberprofile(Idno VARCHAR(100),OtherColumn VARCHAR(100));
INSERT INTO #memberprofile VALUES('N3','Test North 3'),('S24','Test South 24'),('N14','Test North 14')
DECLARE #Zone VARCHAR(20)='N';
SELECT *
,ZoneCode + REPLACE(STR(Number,5),' ','0') AS YourNewPaddedCode
FROM #memberprofile
CROSS APPLY
(
SELECT LEFT(Idno,1) AS ZoneCode
,CAST(SUBSTRING(Idno,2,1000) AS INT) AS Number
) AS Idno_in_parts
WHERE ZoneCode=#Zone;
GO
--Clean up
--DROP TABLE #memberprofile
The result
Idno OtherColumn ZoneCode Number YourNewPaddedCode
N3 Test North 3 N 3 N00003
N14 Test North 14 N 14 N00014

Related

How can we read a varchar column, take the integer part out and add new column incrementing that integer part using script

I need to write a SCRIPT for below scenario:
We have a column X with rows value for this column X as X01,X02,X03,X04........
The problem I am stuck with is that I needed to add another row to this table based on the value of the last row that is X04, Well I am able to identify the logic that I need to work which is given below:
I need to read value X04
Take the integer part 04
Increment by 1 => 05
Save column value as X05
I am able to pass with the 1st step which is not very hard. The problem that I am facing is the next steps. I have researched and tried quite a lot commands but none worked.
Any help is highly appreciated. Thanks.
You seem to be describing:
select concat(left(max(x), 1),
right(concat('00', try_convert(int, right(max(x), 2)) + 1), 2)
from t;
This is doing the following:
Taking the left most character.
Converting the two right characters to a number and adding one.
Converting that back to a zero-padded string.
Here is a db<>fiddle.
Now: That you want to increment a string value seems broken. You should just use an identity column or sequence to assign a number. You can format the value as a string when you query the table -- or use a computed column to store that.
Try below Script
CREATE TABLE #table (x varchar(20))
INSERT INTO #table VALUES('X01'),('X02'),('X03'),('X04')
DECLARE #maxno NVARCHAR(20)
DECLARE #maxstring NVARCHAR(20)
DECLARE #finalno NVARCHAR(20)
DECLARE #loopminno INT =1 -- you can change based on the requirement
DECLARE #loopmaxno INT =10 -- how many number we want to increment
WHILE #loopminno < #loopmaxno
BEGIN
select #maxno = MAX(CAST(SUBSTRING(x, PATINDEX('%[0-9]%', x), 100) as INT))
, #maxstring = MAX(SUBSTRING(x, 1, PATINDEX('%[0-9]%',x)-1))
from #table
where PATINDEX('%[1-9]%',x)>0
SELECT #finalno = #maxstring + CASE WHEN CAST(#maxno AS INT)<9 THEN '0' ELSE '' END + CAST(#maxno+1 AS VARCHAR(20))
INSERT INTO #table
SELECT #finalno
SET #loopminno = #loopminno+1
END

SQL Server function to parse docket numbers

On our database in a Cases table, the Docket field stores the docket number(s) for each case. Each docket number takes the form such as
AB19-1-000
CD19-1043-000
EF18-24-001
These are comprised of "root" dockets and "sub" dockets. The roots here are
AB19-1
CD19-1043
EF18-24
The root dockets are comprised of the two alpha character docket prefix, which indicates the case type. Followed by a two numerical character code indicating the fiscal year the case was filed. Then a hyphen. Then a "sequence" number (with no fixed # of digits, although none have ever had more than 4 digits) indicating the sequence that the case was filed (relative to other cases of the same type that were filed in that fiscal year).
The final three digits (after the final hyphen) of the overall docket number represent the "subdocket," and are to allow for multiple filings within the docket. The initial filing is always docketed with a 000 subdocket. Subsequent filings within that root docket are subdocketed as 001, 002, 003, etc.
To make things more complicated, there can be multiple docket numbers listed within the Docket field, and (in the horrid legacy database design we have) multiple docket numbers are always separated with exactly one space. (I know. Don't get me started.)
I want to create a tool that will help us generate the docket number for new cases easier/quicker than our current approach (which uses a VBA loop and is very slow). Specifically, I want to write a function for use on SQL Server that will spit out the next sequence number for a new filing of a given type and fiscal year.
The steps would be roughly:
Accept as an argument a two character case type and a two digit fiscal year.
Identify all docket numbers entered where the alpha prefix is the specified case type and the next two characters are the specified fiscal year (ignoring subdocket, which we don't care about here).
Identify the highest existing sequence number for that case type and docket year.
Add one to the identified number, and return that number.
I'm a decent programmer, but my SQL is pretty limited to fairly normal queries. I have very limited experience creating functions. So any help (even a general outline of what this kind of function might looks like and how to create it) is much appreciated.
Here's some code to generate some simple test data.
CREATE TABLE MyCases (
CaseId INTEGER PRIMARY KEY,
Docket VARCHAR(50) not null
);
INSERT INTO MyCases
VALUES
(1, 'XL14-204-001 TS14-1-000 PI14-1-000'),
(2, 'PI14-2-000'),
(3, 'PI14-3-000'),
(4, 'PI14-4-001 XL14-22-000'),
(5, 'PI14-6-000'),
(6, 'PI14-7-000 XL14-382-000'),
(7, 'PI15-1-000 XL15-23-000'),
(8, 'PI15-2-000 TS15-23-000'),
(9, 'PI15-3-000'),
(10, 'PI15-4-000 TS15-2-000')
;
And with the desired function, if the user entered MyFunction('PI',14), the result would be 8, because the highest existing sequential number for all PI14 docket numbers is PI14-7, and adding one to 7 gives 8. Similarly, the result for MyFunction('PI',15) would be 5.
Something like this:
create or alter function MyFunction(#RootDocket char(2), #FiscalYear smallint)
returns int as
begin
declare #NextSequenceNumber int;
with q as
(
select
c.CaseId,
cd.Docket,
RootDocket = left(cd.Docket,2),
FiscalYear = cast(right(left(cd.docket,4),2) as tinyint),
SequenceNumber = cast(substring(cd.Docket,6, charindex('-',cd.Docket,7)-6) as smallint),
SubDocket = cast(right(cd.Docket,3) as smallint)
from dbo.mycases c
cross apply (select value Docket from string_split(Docket,' ') ) cd(Docket)
)
select #NextSequenceNumber = max(SequenceNumber) + 1
from q
where RootDocket = #RootDocket
and FiscalYear = #FiscalYear
return #NextSequenceNumber;
end
go
select dbo.MyFunction('PI',15);
select dbo.MyFunction('PI',14);
outputs
-----------
5
-----------
8
Here is an approach that uses a loop to sequentially check each possible sequence number for the given case type and year. As soon as an available sequence number is found, it is returned.
This might be a little more optimized that what you requested, in the sense that it will fill the gaps in the sequence, if there are any. This might, or might not be what you need.
Code:
CREATE FUNCTION GetNextAvailableSequence (
#case_type VARCHAR(2),
#fiscal_year INT
)
RETURNS INT
AS
BEGIN
DECLARE #seq INT;
DECLARE #done INT;
SET #done = 0;
SET #seq = 0;
WHILE #done = 0
BEGIN
SET #seq = #seq + 1;
IF (
SELECT COUNT(*)
FROM MyCases
WHERE ' ' + docket LIKE
'% '
+ #case_type
+ CAST(#fiscal_year as VARCHAR(2))
+ '-'
+ CAST(#seq as VARCHAR(2))
+ '%'
) = 0
BEGIN
SET #done = 1;
END;
END;
RETURN #seq;
END;
Demo on DB Fiddle:
SELECT dbo.GetNextAvailableSequence('PI', 14);
| (No column name) |
| ---------------: |
| 5 |
This fills the first gap for PI-14.
select dbo.GetNextAvailableSequence('PI', 15);
| (No column name) |
| ---------------: |
| 5 |
There are no gaps for PI-15, this is the first available sequence.

SQL Real number convert to Ft & In

enter code hereI asked a question back in May about how to convert a number from a table that inches, such as 300.9 to a Ft' In" display. I got two very good answers...
CONVERT(VARCHAR(20),finlength /12) + '''' + CONVERT(VARCHAR(20),finlength %12)+'"' as FinishLen
replace(replace('<feet>'' <inches>', '<feet>', FinLength / 12), '<inches>', FinLength % 12) as FinishLen
Both worked well until I ran into a table that the inches are declared as "REAL" numbers. Now I ran into this error...
"The data types real and int are incompatible in the modulo operator."
How can I display that? I can't change the table declarations. Other users need that data as well.
Thanks and Kuddos for the great site.
Guess the full query might help, sorry.
SELECT TOP 1000 ProdWkYr
,Product
,Grade
,CONVERT(VARCHAR(20),finlength /12) + '''' + CONVERT(VARCHAR(20),finlength %12)+'"' as FinishLen
,BlmWeight
,BlmsNeeded
,BlmFootWgt
FROM NYS2MiscOrderInfo
where ProdWkYr = 3215
order by product, Grade
Just include a floor() in your expression like
-- -------------------------------------------------------------------
-- set-up some test data using a CTE:
WITH tst as ( SELECT 13.7 finlength UNION ALL SELECT 123 )
-- alternatively: generate a table [tst] with a single column [finlength]
-- -------------------------------------------------------------------
SELECT CONVERT(VARCHAR(20),FLOOR(finlength / 12)) + ''''
+ CONVERT(VARCHAR(20),finlength % 12)+'"' as FinishLen
FROM tst
-- results:
FinishLen
1'1.70"
10'3."
This will turn the first (ft) value into an integer while the second one (in) will still show all the digits after the decimal point.
UPDATE
When I ran the select from a #tmp table I got the same error as OP. I then modified and ended up with this:
It is as ugly as hell now, but at least it works now, see here SQL Demo:
create table #tst (finlength float);
INSERT INTO #tst VALUES (13.7),(123.),(300.9);
SELECT CONVERT(VARCHAR(20),FLOOR(finlength / 12)) + '''' -- ft
+CONVERT(VARCHAR(20),finlength-FLOOR(finlength) -- in: fractional part
+CAST(FLOOR(finlength) as int) %12)+'"' -- in: integer part
as FinishLen
FROM #tst
Please note: The formula will return reasonable results for positive values. For "negative distances" further changes are necessary. If similar output is required in different places then a UDF makes sense here. Something like:
CREATE FUNCTION ftinstr(#v float) RETURNS varchar(32) BEGIN
DECLARE #l int;
SELECT #l=FLOOR(ABS(#v));
RETURN CAST(SIGN(#v)*(#l/12) AS varchar(6))+''''
+CAST( ABS(#v)-#l+#l%12 AS varchar(20))+'"'
END
would do the trick, To be called like dbo.ftinstr( floatval ).
Maybe I can beautify it a little still ...

SQL query--String Permutations

I am trying to create a query using a db on OpenOffice where a string is entered in the query, and all permutations of the string are searched in the database and the matches are displayed. My database has fields for a word and its definition, so if I am looking for GOOD I will get its definition as well as the definition for DOG.
You'll need a third column as well. In this column you'll have the word - but with the letters sorted in alphabetical order. For example, you'll have the word APPLE and in the next column the word AELPP.
You would sort the word your looking for - and run a some SQL code like
WHERE sorted_words = 'my_sorted_word'
for the word apple, you would get something like this:
unsorted sorted
AELPP APPLE
AELPP PEPLA
AELPP APPEL
Now, you also wanted - correct me if I'm wrong, but you want all the words that can be made with **any combination ** of the letters, meaning APPLE also returns words like LEAP and PEA.
To do this, you would have to use some programming language - you would have to write a function that preformed the above recursively, for example - for the word AELLP you have
ELLP
ALLP
AELP
and so forth.. (each time subtracting one letter in every combination, and then two letters in every combination possible ect..)
Basically, you can't easily do permutations in single SQL statement. You can easily do them in another language though, for example here's how to do it in C#: http://msdn.microsoft.com/en-us/magazine/cc163513.aspx
Ok, corrected version that I think handles all situations. This will work in MS SQL Server, so you may need to adjust it for your RDBMS as far as using the local table and the REPLICATE function. It assumes a passed parameter called #search_string. Also, since it's using VARCHAR instead of NVARCHAR, if you're using extended characters be sure to change that.
One last point that I'm just thinking of now... it will allow duplication of letters. For example, "GOOD" would find "DODO" even though there is only one "D" in "GOOD". It will NOT find words of greater length than your original word though. In other words, while it would find "DODO", it wouldn't find "DODODO". Maybe this will give you a starting point to work from though depending on your exact requirements.
DECLARE #search_table TABLE (search_string VARCHAR(4000))
DECLARE #i INT
SET #i = 1
WHILE (#i <= LEN(#search_string))
BEGIN
INSERT INTO #search_table (search_string)
VALUES (REPLICATE('[' + #search_string + ']', #i)
SET #i = #i + 1
END
SELECT
word,
definition
FROM
My_Words
INNER JOIN #search_table ST ON W.word LIKE ST.search_string
The original query before my edit, just to have it here:
SELECT
word,
definition
FROM
My_Words
WHERE
word LIKE REPLICATE('[' + #search_string + ']', LEN(#search_string))
maybe this can help:
Suppose you have a auxiliary Numbers table with integer numbers.
DECLARE #s VARCHAR(5);
SET #s = 'ABCDE';
WITH Subsets AS (
SELECT CAST(SUBSTRING(#s, Number, 1) AS VARCHAR(5)) AS Token,
CAST('.'+CAST(Number AS CHAR(1))+'.' AS VARCHAR(11)) AS Permutation,
CAST(1 AS INT) AS Iteration
FROM dbo.Numbers WHERE Number BETWEEN 1 AND 5
UNION ALL
SELECT CAST(Token+SUBSTRING(#s, Number, 1) AS VARCHAR(5)) AS Token,
CAST(Permutation+CAST(Number AS CHAR(1))+'.' AS VARCHAR(11)) AS
Permutation,
s.Iteration + 1 AS Iteration
FROM Subsets s JOIN dbo.Numbers n ON s.Permutation NOT LIKE
'%.'+CAST(Number AS CHAR(1))+'.%' AND s.Iteration < 5 AND Number
BETWEEN 1 AND 5
--AND s.Iteration = (SELECT MAX(Iteration) FROM Subsets)
)
SELECT * FROM Subsets
WHERE Iteration = 5
ORDER BY Permutation
Token Permutation Iteration
----- ----------- -----------
ABCDE .1.2.3.4.5. 5
ABCED .1.2.3.5.4. 5
ABDCE .1.2.4.3.5. 5
(snip)
EDBCA .5.4.2.3.1. 5
EDCAB .5.4.3.1.2. 5
EDCBA .5.4.3.2.1. 5
(120 row(s) affected)

SQL Server substring breaking on words, not characters

I'd like to show no more than n characters of a text field in search results to give the user an idea of the content. However, I can't find a way to easily break on words, so I wind up with a partial word at the break.
When I want to show: "This student has not submitted his last few assignments", the system might show: "This student has not submitted his last few assig"
I'd prefer that the system show up to the n character limit where words are preserved, so I'd like to see:
"This student has not submitted his last few"
Is there a nearest word function that I could write in T-SQL, or should I do that when I get the results back into ASP or .NET?
If you must do it in T-SQL:
DECLARE #t VARCHAR(100)
SET #t = 'This student has not submitted his last few assignments'
SELECT LEFT(LEFT(#t, 50), LEN(LEFT(#t, 50)) - CHARINDEX(' ', REVERSE(LEFT(#t, 50))))
It will not be catastrophically slow, but it will definitely be slower than doing it in the presentation layer.
Other than that — just cutting off the word and appending an ellipsis for longer strings is no bad option either. This way at least all truncated strings have the same length, which might come in handy if you are formatting for a fixed-width output.
I agree with doing this outside of the database that way other applications with different length restrictions can make their own decisions on what to show/hide. Perhaps that can be a parameter to the database call though.
Here's a quick stab at a solution:
DECLARE #OriginalData NVARCHAR(MAX)
,#ReversedData NVARCHAR(MAX)
,#MaxLength INT
,#DelimiterPosition INT ;
SELECT #OriginalData = 'This student has not submitted his last few assignments'
,#MaxLength = 45;
SET #ReversedData = REVERSE(
LEFT(#OriginalData, #MaxLength)
);
SET #DelimiterPosition = CHARINDEX(' ', #ReversedData);
PRINT LEFT(#OriginalData, #MaxLength - #DelimiterPosition);
/*
This student has not submitted his last few assignments
1234567890123456789012345678901234567890123456789012345
*/
I recommend doing that kind of logic outside database. With C# it could look similar to this:
static string Cut(string s, int length)
{
if (s.Length <= length)
{
return s;
}
while (s[length] != ' ')
{
length--;
}
return s.Substring(0, length).Trim();
}
Of cause you could do this with T-SQL, but that is bad idea (bad performance etc.). If you really need to put it inside DB I would use CLR-based stored procedure instead.
I'd like to add to the solutions already offered that word breaking logic is a lot more complicated than it seems on the surface. To do it well you are going to need to define a number of rules for what constitutes a word. Consider the following:
Spaces - No brainer.
Hyphens - Well that depends. In Over-exposed proably, in re-animated probably not. Then what about dates such as 01-02-1985?
Periods - No brainer. Oh wait, what about the one in myemail#myisp.com or $79.95?
Commas - In numbers such as 1,239 no, but in sentences yes.
Apostrophes - In O'Reily no, in SQL is an 'Enterprise' Database tool yes.
Do special characters alone constitute words?: In Item 1 : Buy TP is the colon counted as a word?
I found an answer on this site and modified it:
the cast (150) must be greater than the number of characters you're returning (100)
LEFT (Cast(myTextField As varchar(150)),
CHARINDEX(' ', CAST(flag_myTextField AS VARCHAR(150)), 100) ) AS myTextField_short
I'm not sure how fast this will run, but it will work....
DECLARE #Max int
SET #Max=??
SELECT
REVERSE(RIGHT(REVERSE(LEFT(YourColumnHere,#Max)),#Max- CHARINDEX(' ',REVERSE(LEFT(YourColumnHere,#Max)))))
FROM YourTable
WHERE X=Y
I wouldn't advice to do that either, but if you must, you can do something like this:
DECLARE #text nvarchar(max);
DECLARE #end_char int;
SELECT #text = 'This student has not submitted his last few assignments', #end_char = 50 ;
WHILE #end_char > 0 AND SUBSTRING( #text, #end_char+1, 1 ) <> ' '
SET #end_char = #end_char - 1
SELECT #text = SUBSTRING( #text, 1, #end_char ) ;
SELECT #text