Get max number from table add one and check with specific convention - sql

I have to produce artikel number based on some convention, and this convention is as below
The number of digits
{1 or 2 or 3}.{4 or 5}.{n}
example products numbers:
7.1001.1
1.1453.1
3.5436.1
12.7839.1
12.3232.1
13.7676.1
3.34565.1
12.56433.1
247.23413.1
The first part is based on producent, and every producent has its own number. Let's say Rebook - 12, Nike - 256 and Umbro - 3.
I have to pass this number and check in table if there are some rows containing it e.g i pass 12 then i should get everything which starts from 12.
and now there should be three cases what to do:
1st CASE: no rows at the table:
then retrieve 1001
2nd case: if there are rows
so for sure there is already at least one:
12.1001.1
and more if they are let's say:
12.1002.1
12.1003.1
...
12.4345.1
so should be retreived next one so: 4346
and if there are already 5-digits for this product so let's say:
12.1002.1
12.1003.1
...
12.9999.1
so should be retreived next one so: 10001
3rd case: in fact same as 2nd but if it rached 9999 for second part:
12.1001.1
...
12.9999.1
then returned should be: 10001
or
12.1002.1
12.1003.1
...
12.9999.1
12.10001.1
12.10002.1
so should be retreived next one so: 10003
Hope you know what i mean
I already have started something. This code is taking producent number - looking for all rows starting with it and then just simply adding 1 to the second part unfortunetly i am not sure how should i change it according to those 3 cases.
select
parsename(max(nummer), 3) + '.' -- 3
+ ltrim(max(cast(parsename(nummer, 2) as int) +1)) -- 5436 -> 5437
+ '.1'
from tbArtikel
where Nummer LIKE '3.%'
Counting on your help. If something unclear let me know.
Additional question:
Using cmd As New SqlCommand("SELECT CASE WHEN r.number Is NULL THEN 1001
WHEN r.number = 9999 THEN 10001
Else r.number + 1 End number
FROM (VALUES(#producentNumber)) AS a(art) -- this will search this number within inner query And make case..
LEFT JOIN(
-- Get producent (in Like) number And max number Of it (without Like it Get all producent numbers And their max number out Of all
SELECT PARSENAME(Nummer, 3) art,
MAX(CAST(PARSENAME(Nummer, 2) AS INT)) number
FROM tbArtikel WHERE Nummer Like '#producentNumber' + '[.]%'
GROUP BY PARSENAME(Nummer, 3)
) r
On r.art = a.art", con)
cmd.CommandType = CommandType.Text
cmd.Parameters.AddWithValue("#producentNumber", producentNumber)

A fairly straight forward way is to (ab)use PARSENAME to split the string to be able to extract the current maximum. An outer query can then just implement the rules for the value being missing/9999/other.
The value (12 here) is inserted in a table value constructor to be able to detect a missing value using a LEFT JOIN.
SELECT CASE WHEN r.number IS NULL THEN 1001
WHEN r.number = 9999 THEN 10001
ELSE r.number + 1 END number
FROM ( VALUES(12) ) AS a(category)
LEFT JOIN (
SELECT PARSENAME(prodno, 3) category,
MAX(CAST(PARSENAME(prodno, 2) AS INT)) number
FROM products
GROUP BY PARSENAME(prodno, 3)
) r
ON r.category = a.category;
An SQLfiddle to test with.
As a further optimization, you could add a WHERE prodno LIKE '12[.]%' in the inner query to not parse through un-necessary rows.

I don't fully understand what you're asking for. I am unsure about the examples...but if i was doing it I'd try to break the field into 3 fields first and then do something with them.
sqlfiddle
SELECT nummer,LEFT(nummer,first-1) as field1,
RIGHT(LEFT(nummer,second-1),second-first-1) as field2,
RIGHT(nummer,LEN(nummer)-second) as field3
FROM
(SELECT nummer,
CHARINDEX('.',nummer) as first,
CHARINDEX('.',nummer,CHARINDEX('.',nummer)+1)as second
from tbArtikel)T
Hopefully with the 3 fields broken up, it's much easier to apply logics to them now.
update:
Okay i reread your question and i sort of know what you're trying to get at..
if user search for a value that doesn't exist for example 8.
Then you want 1001 returned
if they search for anything else that has results then return the max+1
unless it's 9999 then return 10001.
If this is correct then check this sqlfiddle2
DECLARE #search varchar(20)
SET #search = '8'
SELECT field1,max(nextvalue) as nextvalue FROM
(SELECT field1,
MAX(CASE (field2)
WHEN 9999 THEN 10001
ELSE field2+1
END) as nextvalue
FROM
(SELECT nummer,
CAST(LEFT(nummer,first-1) as INTEGER) as field1,
CAST(RIGHT(LEFT(nummer,second-1),second-first-1) as INTEGER) as field2,
CAST(RIGHT(nummer,LEN(nummer)-second) as INTEGER) as field3
FROM
(SELECT nummer,
CHARINDEX('.',nummer) as first,
CHARINDEX('.',nummer,CHARINDEX('.',nummer)+1)as second
FROM tbArtikel
)T
)T2
GROUP BY field1
UNION
SELECT CAST (#search as INTEGER)as field1 ,1001
)T3
WHERE field1 = #search
GROUP BY field1
Just change the #search variable to see it's results
I think there might be a cleaner way to do this but it's not coming to me right now :(

If you really can't add 2 new fields (is't probably the simplest and fastest solution), and probably can't add functional index, you must extract 2nd part number and get max of this, increment, then concatenate with your condition 1st part number and '.1' at the end:
SELECT :par1 || '.' || (Max(To_Number(SubStr(nummer, dot1 + 1, dot2 - dot1 -1 ))) + 1) || '.1' NEW_number
--SELECT SubStr(nummer, 1, dot1 - 1) N1st, SubStr(nummer, dot1 + 1, dot2 - dot1 -1 ) N2nd, SubStr(nummer, dot2 + 1) N1th
FROM (
SELECT nummer, InStr(nummer, '.') dot1, InStr(nummer, '.', 1, 2) dot2
FROM tbArtikel
WHERE nummer LIKE :par1 || '.%')
;
--GROUP BY SubStr(nummer, 1, dot1 – 1)
it was for oracle sql, i don't have sql-serwer to test, but probably this is simplest answer:
select #par1 + '.' + (select max(cast(SUBSTRING(nummer, CHARINDEX( '.', nummer, 1 ) +1, CHARINDEX( '.', nummer, CHARINDEX( '.', nummer, 1 ) +1 ) - CHARINDEX( '.', nummer, 1 ) -1) as int)) + 1 from tbArtikel where nummer LIKE #par1 || '.%') + '.1'
if parsename(nummer, 2) is you defined function to get 2nd number then:
select #parm + '.' + (max(cast(parsename(nummer, 2) as int)) + 1) + '.1'
from tbArtikel
where Nummer LIKE #parm + '.%'

Related

Remove Alphabet and Insert Leading 0 up to Certain Character Length

I have a table name Table and column name Item. Below is the data.
ABC123
ABC1234
ABC12345
HA11
K112
L1164
I need to remove the alphabets, replace them with leading 0, and the total character length must be 9. Below is the results.
000000123
000001234
000012345
000000011
000000112
000001164
I know how to change for ABC (certain alphabet set) only however I don't know to build the CASE statement. Below is what I have been successful.
select REPLICATE('0',9-LEN(A.B)) + A.B
from
(select replace(Item, 'ABC','') as B from Table) as A
I tried to combine CASE with SELECT and it doesn't seem like it.
Case when Item like '%ABC%' then
select REPLICATE('0',9-LEN(A.B)) + A.B
from
(select replace(Item, 'ABC','') as B from Table) as A
when Item like '%HA%' then
select REPLICATE('0',9-LEN(A.B)) + A.B
from
(select replace(Item, 'HA','') as B from Table) as A
when Item like '%K%' then
select REPLICATE('0',9-LEN(A.B)) + A.B
from
(select replace(Item, 'K','') as B from Table) as A
when Item like '%L%' then
select REPLICATE('0',9-LEN(A.B)) + A.B
from
(select replace(Item, 'L','') as B from Table) as A
else Item
End
Does anyone know how to achieve the result? I'm using SQL Server 2012.
Thank you.
I assumed, that you have letters only at the beginning of your data.
declare #s varchar(20) = 'ABS123'
-- we find index of first occurence of digit and then we cut out the letters
set #s = right(#s, len(#s) - patindex('%[0-9]%', #s) + 1)
-- here we just produce string with amount of zeros we need
select left('000000000', 9 - len(#s)) + #s
In terms of applying it to your table:
select left('000000000', 9 - len([Digits])) + [Digits] from (
select right([Item], len([Item]) - patindex('%[0-9]%', [Item]) + 1) as [Digits] from [Table]
)

SQL Server: CTE tree with priority

I have some troubles how to build a query for a recursive parent-child tree in SQL Server.
I have found some common table expression but i have to consider another parameter (the priority in each level) that i have not found from a search over the internet, so the structure of my table is like this
id - parentid - proprity
For example if i have this data:
1 - NULL - 2
2 - NULL - 1
3 - 2 - 2
4 - 3 - 1
5 - 2 - 0
6 - 1 - 0
7 - 2 - 3
The query must return this at right order:
-2
--5
--3
---4
--7
-1
--6
Also this list is orderable so if any order is change I have to reset the priority at right place. Anyone has already a solution for this case with queries in SQL Server?
Thanks in advance
This will give you the order that you need. Personally, I'd try to do that ordering in the display layer, but this will do it in SQL.
This relies on single-digit priority values. If those might be larger then you'll need to prepend leading 0s to the priorities, like: RIGHT('0' + priority, 2).
The query just builds a string of the priorities at each point so that you can then order off of it.
;WITH Hierarchy_CTE AS
(
SELECT
id,
parent_id,
CAST([priority] AS VARCHAR(100)) AS priority_string,
id AS base,
1 AS lvl
FROM
My_Table
WHERE
parent_id IS NULL
UNION ALL
SELECT
MT.id,
MT.parent_id,
CAST(H.priority_string + CAST(MT.priority AS VARCHAR(20)) AS VARCHAR(100)),
H.base,
H.lvl + 1
FROM
Hierarchy_CTE H
INNER JOIN My_Table MT ON MT.parent_id = H.id
)
SELECT
id,
base,
lvl
FROM
Hierarchy_CTE
ORDER BY
priority_string
It seems that this solution is working (taken from Tom H answer):
;WITH Hierarchy_CTE AS
(
SELECT
id,
parent_id,
CAST(REPLICATE('0', 10 - LEN(CAST([Order] AS VARCHAR))) + CAST([Order] AS VARCHAR) + '.' +
REPLICATE('0', 10 - LEN(CAST(MerchantCategoryId AS VARCHAR))) + CAST(MerchantCategoryId AS VARCHAR) AS VARCHAR(4000)) AS priority_string,
id AS base,
1 AS lvl
FROM
My_Table
WHERE
parent_id IS NULL
UNION ALL
SELECT
MT.id,
MT.parent_id,
CAST(H.priority_string + '|' + REPLICATE('0', 10 - LEN(CAST(MT.[priority] AS VARCHAR))) + CAST(MT.[priority] AS VARCHAR) + '.' +
REPLICATE('0', 10 - LEN(CAST(MT.id AS VARCHAR))) + CAST(MT.id AS VARCHAR) AS VARCHAR(4000)) AS priority_string,
H.base,
H.lvl + 1
FROM
Hierarchy_CTE H
INNER JOIN My_Table MT ON MT.parent_id = H.id
)
SELECT
id,
base,
lvl
FROM
Hierarchy_CTE
ORDER BY
priority_string
I added padding zeros equal to a fixed length minus then "stringified" priority length and also for the id, so i can order them to the priority and then for id.
This solution works only in ascending orders (not work for descending orders with the maintenance of a tree view), but this list is useful for me only when i have to save updated priorities, so it may be enough.

using charindex in a substring to trim a string

There is a column name from which I want to use to make a new column.
example:
name
asd_abceur1mz_a
asd_fxasdrasdusd3mz_a
asd_abceur10yz_a
asd_fxasdrasdusd15yz_a
The length of the column is not fixed so I assumed i have to use charindex to have a reference point from which I could trim.
What i want: at the end there is always z_a, and i need to place in a separate column the left part from z_a like this:
nameNew
eur1m
usd3m
eur10y
usd15y
The problem is that the number (in this example 1, 3, 10, 15) has 1 or two digits. I need to extract the information from name to nameNew.
After that i was thinking to make it easier to read and to output it like this:
eur_1m
usd_3m
eur_10y
usd_15y
I tried using a combination of substring and charindex, but so far without success.
SELECT *
, SUBSTRING(name, 1, ( CHARINDEX('z_a', NAME) - 1 )) AS nameNew
FROM myTable
This is for the first step, trimming the string, for the 2nd step (making it easier to read) I don't know how to target the digit and place an _.
Any help would be appreciated. Using sql server 2012
edit:
First of all thank you for your time and solutions. But your queries more or less even if they are working for 1 or 2 digits have the same problem. Consider this situation:
name
ab_dertEUR03EUR10YZ_A
if eur is two times in the string, then how can I eliminate this? Sorry for not includding this in my original post but i forgot that situation is possible and now that's a problem.
edit:
test your queries here, on this example:
http://www.sqlfiddle.com/#!3/21610/1
Please note that at the end it can be any combination of 1 or 2 digits and the letter y or m.
Ex: ab_rtgtEUR03EUR2YZ_A , ab_rtgtEUR03EUR2mZ_A, ab_rtgtEUR03EUR20YZ_A, ab_rtgtEUR03EUR20mZ_A
Some values for testing:
('ex_CHFCHF01CHF10YZ_A'), ('ab_rtgtEUR03EUR2YZ_A'), ('RON_asdRON2MZ_A'),
('tg_USDUSD04USD5YZ_A');
My understanding of your queries is that they perform something simillar to this (or at least they should)
ex_CHFCHF01CHF10YZ_A -> ex_CHFCHF01CHF10Y -> Y01FHC10FHCFHC -> Y01FHC -> CHF01Y -> CHF_01Y
RON_asdRON2MZ_A -> RON_asdRON2M -> M2NORdsa_ron -> M2NOR -> RON2M -> RON_2M
This works for one or two digits:
stuff(case
when name like '%[0-9][0-9]_z[_]a'
then left(right(name, 9), 6)
when name like '%[0-9]_z[_]a'
then left(right(name, 8), 5)
end, 4, 0, '_')
You can use a combination of substring , reverse and charindex.
SQL Fiddle
select substring(namenew,1,3) + '_' + substring(namenew, 4, len(namenew))
from (
select
case when name like '%[0-9][0-9]_z[_]a' then
reverse(substring(reverse(name), charindex('a_z',reverse(name)) + 3, 6))
when name like '%[0-9]_z[_]a' then
reverse(substring(reverse(name), charindex('a_z',reverse(name)) + 3, 5))
end as namenew
from myTable
) t
Try it like this:
declare #tbl TABLE(name VARCHAR(100));
insert into #tbl VALUES
('asd_abceur1mz_a')
,('asd_fxasdrasdusd3mz_a')
,('asd_abceur10yz_a')
,('asd_fxasdrasdusd15yz_a')
,('ab_dertEUR03EUR10YZ_A');
WITH CutOfThreeAtTheEnd AS
(
SELECT LEFT(name,LEN(name)-3) AS nameNew
FROM #tbl
)
,Max6CharsFromEnd AS
(
SELECT RIGHT(nameNew,6) AS nameNew
FROM CutOfThreeAtTheEnd
)
SELECT nameNew
,FirstNumber.Position
,Parts.*
,Parts.FrontPart + '_' + Parts.BackPart AS FinalString
FROM Max6CharsFromEnd
CROSS APPLY
(
SELECT MIN(x)
FROM
(
SELECT CHARINDEX('0',nameNew,1) AS x
UNION SELECT CHARINDEX('1',nameNew,1)
UNION SELECT CHARINDEX('2',nameNew,1)
UNION SELECT CHARINDEX('3',nameNew,1)
UNION SELECT CHARINDEX('4',nameNew,1)
UNION SELECT CHARINDEX('5',nameNew,1)
UNION SELECT CHARINDEX('6',nameNew,1)
UNION SELECT CHARINDEX('7',nameNew,1)
UNION SELECT CHARINDEX('8',nameNew,1)
UNION SELECT CHARINDEX('9',nameNew,1)
) AS tbl
WHERE x>0
) AS FirstNumber(Position)
CROSS APPLY(SELECT SUBSTRING(nameNew,FirstNumber.Position,1000) AS BackPart
,SUBSTRING(nameNew,FirstNumber.Position-3,3) AS FrontPart) AS Parts
this is the result:
nameNew Position BackPart FrontPart FinalString
ceur1m 5 1m eur eur_1m
dusd3m 5 3m usd usd_3m
eur10y 4 10y eur eur_10y
usd15y 4 15y usd usd_15y
EUR10Y 4 10Y EUR EUR_10Y

Check palindrome without using string functions with condition

I have a table EmployeeTable.
If I want only that records where employeename have character of 1 to 5
will be palindrome and there also condition like total character is more then 10 then 4 to 8 if character less then 7 then 2 to 5 and if character less then 5 then all char will be checked and there that are palindrome then only display.
Examples :- neen will be display
neetan not selected
kiratitamara will be selected
I try this something on string function like FOR first case like name less then 5 character long
SELECT SUBSTRING(EmployeeName,1,5),* from EmaployeeTable where
REVERSE (SUBSTRING(EmployeeName,1,5))=SUBSTRING(EmployeeName,1,5)
I want to do that without string functions,
Can anyone help me on this?
You need at least SUBSTRING(), I have a solution like this:
(In SQL Server)
DECLARE #txt varchar(max) = 'abcba'
;WITH CTE (cNo, cChar) AS (
SELECT 1, SUBSTRING(#txt, 1, 1)
UNION ALL
SELECT cNo + 1, SUBSTRING(#txt, cNo + 1, 1)
FROM CTE
WHERE SUBSTRING(#txt, cNo + 1, 1) <> ''
)
SELECT COUNT(*)
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY cNo DESC) as cRevNo
FROM CTE t1 CROSS JOIN
(SELECT Max(cNo) AS strLength FROM CTE) t2) dt
WHERE
dt.cNo <= dt.strLength / 2
AND
dt.cChar <> (SELECT dti.cChar FROM CTE dti WHERE dti.cNo = cRevNo)
The result will shows the count of differences and 0 means no differences.
Note :
Current solution is Non-Case-Sensitive for change it to a Case-Sensitive you need to check the strings in a case-sensitive collation like Latin1_General_BIN
You can use this solution as a SVF or something like that.
I dont realy understand why you dont want to use string functions in your query, but here is one solution. Compute everything beforehand:
Add Column:
ALTER TABLE EmployeeTable
ADD SubString AS
SUBSTRING(EmployeeName,
(
CASE WHEN LEN(EmployeeName)>10
THEN 4
WHEN LEN(EmployeeName)>7
THEN 2
ELSE 1 END
)
,
(
CASE WHEN LEN(EmployeeName)>10
THEN 8
WHEN LEN(EmployeeName)>7
THEN 5
ELSE 5 END
)
PERSISTED
GO
ALTER TABLE EmployeeTable
ADD Palindrome AS
REVERSE(SUBSTRING(EmployeeName,
(
CASE WHEN LEN(EmployeeName)>10
THEN 4
WHEN LEN(EmployeeName)>7
THEN 2
ELSE 1 END
)
,
(
CASE WHEN LEN(EmployeeName)>10
THEN 8
WHEN LEN(EmployeeName)>7
THEN 5
ELSE 5 END
)) PERSISTED
GO
Then your query will looks like:
SELECT * from EmaployeeTable
where Palindrome = SubString
BUT!
This is not a good idea. Please tell us, why you dont want to use string functios.
You could do it building a list of palindrome words using a recursive query that generates palindrome words till a length o n characters and then selects employees with the name matching a palindrome word. This may be a really inefficient way, but it does the trick
This is a sample query for Oracle, PostgreSQL should support this feature as well with little differences on syntax. I don't know about other RDBMS.
with EmployeeTable AS (
SELECT 'ADA' AS employeename
FROM DUAL
UNION ALL
SELECT 'IDA' AS employeename
FROM DUAL
UNION ALL
SELECT 'JACK' AS employeename
FROM DUAL
), letters as (
select chr(ascii('A') + rownum - 1) as letter
from dual
connect by ascii('A') + rownum - 1 <= ascii('Z')
), palindromes(word, len ) as (
SELECT WORD, LEN
FROM (
select CAST(NULL AS VARCHAR2(100)) as word, 0 as len
from DUAL
union all
select letter as word, 1 as len
from letters
)
union all
select l.letter||p.word||l.letter AS WORD, len + 1 AS LEN
from palindromes p
cross join letters l
where len <= 4
)
SEARCH BREADTH FIRST BY word SET order1
CYCLE word SET is_cycle TO 'Y' DEFAULT 'N'
select *
from EmployeeTable
WHERE employeename IN (
SELECT WORD
FROM palindromes
)
DECLARE #cPalindrome VARCHAR(100) = 'SUBI NO ONIBUS'
SET #cPalindrome = REPLACE(#cPalindrome, ' ', '')
;WITH tPalindromo (iNo) AS (
SELECT 1
WHERE SUBSTRING(#cPalindrome, 1, 1) = SUBSTRING(#cPalindrome, LEN(#cPalindrome), 1)
UNION ALL
SELECT iNo + 1
FROM tPalindromo
WHERE SUBSTRING(#cPalindrome, iNo + 1, 1) = SUBSTRING(#cPalindrome, LEN(#cPalindrome) - iNo, 1)
AND LEN(#cPalindrome) > iNo
)
SELECT IIF(MAX(iNo) = LEN(#cPalindrome), 'PALINDROME', 'NOT PALINDROME')
FROM tPalindromo

Removing text and adding to front of string

How would I go about removing a string from a field in a sql query (using sql server 2005) and adding it to the front of the string?
For instance my search string field contains: 22378MA
I want to search for the following characters 'MA' in this case.
I would like the query to add this string to the front so that it returns a query like this:
MA2237
My field name is sku for the query.
Not sure I explained myself properly. I don't want to change the field only what is returned in the query in a view. In addition the field value changes so I can't hardcode the sku. In addition the sku length field length is variable. The suffix 'MA' may be changed for certain queries so I need to be able to use it in a case statement.
select SKU as OldSKU, case
when CHARINDEX('MA', SKU) = LEN(SKU) - 1
then 'MA' + SUBSTRING(SKU, 1, LEN(SKU) - 2)
when CHARINDEX('B', SKU) = LEN(SKU)
then 'B' + SUBSTRING(SKU, 1, LEN(SKU) - 1)
when CHARINDEX('XYZ', SKU) = LEN(SKU) - 2
then 'XYZ' + SUBSTRING(SKU, 1, LEN(SKU) - 3)
else SKU
end as NewSKU
from (
select '22378MA' as SKU
union all
select '22378B'
union all
select '22378XYZ'
union all
select '22378TT'
) a
Output:
OldSKU NewSKU
-------- -----------
22378MA MA22378
22378B B22378
22378XYZ XYZ22378
22378TT 22378TT
Alternatively if your suffix is always a text string after numerics, you could use:
;with data as
(
SELECT '22378MA' as sku UNION ALL
SELECT '22444378B' as sku UNION ALL
SELECT '12345GHJ' as sku UNION ALL
SELECT '78456M' as sku
)
SELECT
sku
,RIGHT(sku,LEN(sku) - PATINDEX('%[A-Za-z]%',sku) + 1) + '' + LEFT(sku,PATINDEX('%[A-Za-z]%',sku) - 1) as sku2
from data
Which will put the text (however long it is) before the numbers in the string
Try this to see if you get the results you want
select sku,substring(sku,6,7)+substring(sku,1,5)
from table
If it works OK
update table set sku = substring(sku,6,7)+substring(sku,1,5)