This question already has answers here:
TSQL Generate 5 character length string, all digits [0-9] that doesn't already exist in database
(6 answers)
Closed 7 years ago.
i want to create a coupon code generator by using SQL database but i don't know how to generate 1000 of random number without repeating them. so can someone help me it's important. thanks
Records in a relational database tables are unordered by nature.
therefor, you can simply create a table that has all the values between #First and #Last (0 and 9999 in your case), and then use a random order by when selecting from that table. you can also use a simple int in the database table and just format it when you select the data from the table.
Since my main database is Sql server, and I have no experience with sqlite, I will use Sql Server syntax in my code example, and leave it up to you to find the sqllite equivalent.
First, create the table:
CREATE TABLE Tbl
(
IntValue int PRIMARY KEY,
IsUsed bit NOT NULL DEFAULT 0
)
Then, populate it with numbers between 0 and 9999:
;With CTE AS (
SELECT 0 As IntValue
UNION ALL
SELECT IntValue + 1
FROM CTE
WHERE IntValue + 1 < 10000
)
INSERT INTO Tbl (IntValue)
SELECT IntValue
FROM CTE
OPTION(MAXRECURSION 0)
Then, you want to select multiple values each time, so I would write a stored procedure like this:
CREATE PROCEDURE stp_GetCouponCodes
(
#Number int = 5 -- or whatever number is default
)
AS
BEGIN
DECLARE #UsedValues AS TABLE
(
IntValue int
)
BEGIN TRY
BEGIN TRANSACTION
INSERT INTO #UsedValues
SELECT TOP(#Number) IntValue
FROM Tbl
WHERE IsUsed = 0
ORDER BY NEWID()
UPDATE Tbl
SET IsUsed = 1
FROM Tbl
INNER JOIN
#UsedValues uv ON(Tbl.IntValue = uv.IntValue)
SELECT RIGHT('00000' + CAST(IntValue as varchar), 5)
FROM #UsedValues
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
END
Then, when ever you want to generate coupons, simply execute the stored procedure with the number of coupons you want:
EXEC stp_GetCouponCodes 10;
See working fiddle example here.
The code below uses a quick method to generate 100 random 5-character strings based on the alphabet provided. You'll still need to perform duplicate checking, but this should get you started.
DECLARE #Quantity INT = 1000
DECLARE #Alphabet VARCHAR(100) = '0123456789'
DECLARE #Length INT = LEN(#Alphabet)
DECLARE #Top INT = SQRT(#Quantity) + 1
;WITH CTE AS (
SELECT TOP (#Top) *
FROM sys.objects
)
SELECT TOP (#Quantity)
SUBSTRING(#Alphabet, ABS(CHECKSUM(NEWID())) % #Length + 1, 1)
+ SUBSTRING(#Alphabet, ABS(CHECKSUM(NEWID())) % #Length + 1, 1)
+ SUBSTRING(#Alphabet, ABS(CHECKSUM(NEWID())) % #Length + 1, 1)
+ SUBSTRING(#Alphabet, ABS(CHECKSUM(NEWID())) % #Length + 1, 1)
+ SUBSTRING(#Alphabet, ABS(CHECKSUM(NEWID())) % #Length + 1, 1)
AS [Code]
FROM CTE X
CROSS JOIN CTE Y
Related
I have an nvarchar(200) called ColumnA in Table1 that contains, for example, the value:
ABCDEFGHIJKLMNOPQRSTUVWXYZ
I want to extract every 7 characters into Table2, ColumnB and end up with all of these values below.
ABCDEFG
BCDEFGH
CDEFGHI
DEFGHIJ
EFGHIJK
FGHIJKL
GHIJKLM
HIJKLMN
IJKLMNO
JKLMNOP
KLMNOPQ
LMNOPQR
MNOPQRS
NOPQRST
OPQRSTU
PQRSTUV
QRSTUVW
RSTUVWX
STUVWXY
TUVWXYZ
[Not the real table and column names.]
The data is being loaded to Table1 and Table2 in an SSIS Package, and I'm puzzling whether it is better to do the string handling in TSQL in a SQL Task or parse out the string in a VB Script Component.
[Yes, I think we're the last four on the planet using VB in Script Components. I cannot persuade the other three that this C# thing is here to stay. Although, maybe it is a perfect time to go rogue.]
You can use a recursive CTE calculating the offsets step by step and substring().
WITH
cte
AS
(
SELECT 1 n
UNION ALL
SELECT n + 1 n
FROM cte
WHERE n + 1 <= len('ABCDEFGHIJKLMNOPQRSTUVWXYZ') - 7 + 1
)
SELECT substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ', n, 7)
FROM cte;
db<>fiddle
If you have a physical numbers table, this is easy. If not, you can create a tally-on-the-fly:
DECLARE #string VARCHAR(100)='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
--We create the tally using ROW_NUMBER against any table with enough rows.
WITH Tally(Nmbr) AS
(SELECT TOP(LEN(#string)-6) ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) FROM master..spt_values)
SELECT Nmbr
,SUBSTRING(#string,Nmbr,7) AS FragmentOf7
FROM Tally
ORDER BY Nmbr;
The idea in short:
The tally returns a list of numbers from 1 to n (n=LEN(#string)-6). This Number is used in SUBSTRING to define the starting position.
You can do it with T-SQL like this:
DECLARE C CURSOR LOCAL FOR SELECT [ColumnA] FROM [Table1]
OPEN C
DECLARE #Val nvarchar(200);
FETCH NEXT FROM C into #Val
WHILE ##FETCH_STATUS = 0 BEGIN
DECLARE #I INTEGER;
SELECT #I = 1;
WHILE #I <= LEN(#vAL)-6 BEGIN
PRINT SUBSTRING(#Val, #I, 7)
SELECT #I = #I + 1
END
FETCH NEXT FROM C into #Val
END
CLOSE C
Script Component solution
Assuming that the input Column name is Column1
Add a script component
Open the script component configuration form
Go to Inputs and Outputs Tab
Click on the Output icon and set the Synchronous Input property to None
Add an Output column (example outColumn1)
In the Script editor, use a similar code in the row processing function:
Dim idx as integer = 0
While Row.Column1.length > idx + 7
Output0Buffer.AddRow()
Output0Buffer.outColumn1 = Row.
Column1.Substring(idx,7)
idx +=1
End While
In document management database there is a list of existing files. New files are supposed to get a number, such as SW_01234.xxx. There is a numbering mechanism that serves the new numbers. Question is to find the missing elements - for example if file was deleted.
Existing file names might be completely different and not following the scheme above.
My attempt was to do it this way :
Split existing files at the "dot" - I don't care about .xxx extension, like .doc, .xlsx
Generate a temporary list of SW_00000 to SW_99999
Bring those element that exist in b) but not a)
Sample values
..
SW_00015.PRT
SW_00016.DRW
SW_00020.DRW
SW_00020.PDF
XBC115.DOC
..
I need to get SW_00017, SW_00018, SW_00019 (don't care about the XBC)
Need to have one query in the end
This should get you 99% of the way there. Tweak as necessary. You'll notice that records, 1, 3, 4, and 10 are all missing from the output.
DECLARE #allFiles TABLE (Name VARCHAR(100));
DECLARE #i INT = 0;
DECLARE #dataset TABLE (name VARCHAR(100));
INSERT INTO #dataset
( name )
VALUES ( 'SW_00001.PRT'), ('SW_00003.DRW'), ('SW_00004.DRW'), ('SW_00010.PDF'
);
WHILE #i < 100
BEGIN
INSERT INTO #allFiles
( Name )
VALUES ( 'SW_' + REPLICATE('0',5-LEN(#i)) + CAST(#i AS VARCHAR(10)) -- Name - varchar(100)
);
SET #i = #i + 1;
END;
SELECT *
FROM #allFiles af
WHERE NOT EXISTS (SELECT TOP 1 1 FROM #dataset ds WHERE af.Name = SUBSTRING(ds.name, 0, CHARINDEX('.', ds.name)))
I tried to implement your approach all in one query. In order to do it all in one query, I used CTEs to isolate the document numbers and also to get the range of numbers to use in the "not exists" part. If you need more range in the numbers table, you can get the range by querying differently. See Generate Sequential Set of Numbers
declare #t as table (DocName varchar(50));
insert #t (DocName)
values
('SW_00015.PRT')
,('SW_00016.DRW')
,('SW_00020.DRW')
,('SW_00020.PDF');
/*doing with CTE so the split and substring is more readable, plus needed it anyway for getting the numbers table*/
with isolatedFileNames
as (
/*might be dots in filename, reversing it to isolate the last set (file ext)*/
select DocName
,left(DocName, len(DocName) - charindex('.', reverse(DocName), 0)) as IsolatedDocName
from #t
)
,isolatedNumbers
as (
/*substring to get the number without the prefix*/
select DocName
,IsolatedDocName
,cast(substring(IsolatedDocName, charindex('_', IsolatedDocName, 0) + 1, len(IsolatedDocName)) as int) as IsolatedDocNumber
from isolatedFileNames
)
,numbers
as (
/*use row_number on a large set to get the range*/
select ROW_NUMBER() over (
order by object_id
) + (
/*start at the first document number, change this to 0 if you want to start at 0*/
select min(IsolatedDocNumber) - 1
from isolatedNumbers
) as num
from sys.all_objects
)
,numbersLessThanDocNumbers
as (
select num
from numbers
where num < (
/*limit to max document number in the set*/
select max(IsolatedDocNumber)
from isolatedNumbers
)
)
select num as MissingFromDocumentSet
from numbersLessThanDocNumbers n
where not exists (
select 1
from isolatedNumbers iso
where iso.IsolatedDocNumber = n.num
)
My Table structure is
id type no amount
1 type1 a1 1000
2 type1 a2 2000
3 type2 b1 3000
4 type3 c1 4000
5 type1 a3 5000
6 type2 b2 6000
7 type2 b3 7000
8 type3 c2 8000
now i wants to increment the no field data based on the type.
for example for type1 the next no is a4
and
for numeric only I am using the following code
SELECT ISNULL(Max(No),0)+1 AS No FROM table
but how to do it for with Alphabets in SQL Server 2005
Assuming that prefixes are of single character length, you may try following:
;with cte as (
select type, typePrefix = left(no, 1), typeNum = right(no, len(no) - 1)
from TableName
)
select typePrefix + cast(isnull(max(typeNum), 0) + 1 as varchar(10))
from cte
where type = 'type1'
group by typePrefix
But it will not work if you try to generate next no for a type which is not in table (e.g. 'type4'). To allow it, you may need a separate table, where prefix for each type is specified:
create table TypePrefixes (type varchar(50), prefix varchar(10))
insert into TypePrefixes values ('type1', 'a')
insert into TypePrefixes values ('type2', 'b')
insert into TypePrefixes values ('type3', 'c')
insert into TypePrefixes values ('another_type', 'd')
--etc.
In this case, statement to get next no will look as:
select tp.prefix + cast(isnull(max(cast(right(t.no, len(t.no) - len(tp.prefix)) as int)), 0) + 1 as varchar(20))
from TableName t
right join TypePrefixes tp on tp.type = t.type
where tp.type = 'type4'
group by tp.prefix
Also, you may just wish to calculate no for each record on the fly, like:
;with cte as (
select *,
typeNum = row_number() over (partition by type order by id),
typePrefix = char(dense_rank() over (order by type) + ascii('a') - 1)
from TableName
)
select *, No2 = typePrefix + cast(typeNum as varchar(10))
from cte
However, the latter is limited in number of distinct types in your table, which should not exceed 26 (so that we not go beyond 'z').
try something like
SELECT ISNULL(Max(No),0)+1 AS No FROM table group by type
First, you need an UNIQUE index on No column:
CREATE UNIQUE INDEX IUN_MyTable_On
ON MySchema.MyTable(On);
GO
This unique index will prevent duplicate values but, also, will help the query below.
Second, you could use this script to generate the next No for a given letter:
DECLARE #Chr CHAR(1);
SET #Chr='A';
BEGIN TRY
BEGIN TRANSACTION;
DECLARE #LastId INT;
DECLARE #NewNo VARCHAR(...); -- Fill with No's max. length
-- Previous index will help this query
SELECT #LastId=MAX( CONVERT(INT,SUBSTRING(#LastNo,2,8000)) )
FROM MySchema.MyTable x WITH(UPDLOCK) -- It locks the rows to prevent a concurent session to generate the same value (No)
WHERE x.No LIKE #Chr+'%';
SET #NewNo=#Chr+CONVERT(VARCHAR(11),ISNULL(#LastId,0)+1);
-- Do whatever you want with the new value: ex. INSERT
INSERT INTO ... (No,...)
VALUES (#NewNo,...);
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
DECLARE #ErrMsg NVARCHAR(2000);
SET #ErrMsg=ERROR_MESSAGE();
IF ##TRANCOUNT>0
BEGIN
ROLLBACK;
END
RAISERROR(#ErrMsg,16,1);
END CATCH
Note #1: This solution should be safe if this is the only way to generate the new values (#NewNo).
Note #2: If that SELECT query acquires at least 5000 locks then SQL Server will escalate locks at table/partition level.
I my project i have a session variable that variable contains list of recently accessed values.With help of these values i need to get data from database.For this i wrote a storedprocedure with single parameter(#myparam) but its giving only one row from table.
How can get list of rows from table using session list for storedprocedure?
If i understood you right, try this
CREATE function [dbo].[csv2tbl](#list nvarchar(max), #delimiter nvarchar(10))
returns #res table ([index] int PRIMARY KEY, col nvarchar(max))
AS BEGIN
with tbl_for_csv as
(
select 0 as [index] ,left(#list + #delimiter+#delimiter,charindex(#delimiter,#list + #delimiter+#delimiter) -1)as col,
right(#list + #delimiter+#delimiter,len(#list + #delimiter+#delimiter) - charindex(#delimiter,#list + #delimiter+#delimiter)) as Str
union all
select [index]+1, left(Str,charindex(#delimiter,Str) - 1)as Col,
right(Str,len(Str) - charindex(#delimiter,Str)) from tbl_for_csv
where len(right(Str,len(Str) - charindex(#delimiter,Str))) > 0
)
INSERT #res
select [index], col from tbl_for_csv option (MAXRECURSION 0);
return;
END
GO
SELECT *
from YourTable
JOIN [dbo].[csv2tbl](#recentAssetList, ',') x ON x.col = yourtable.Id
I have a table wuth the folowing structure
PickupPointCode LocationCode ClientCode
1 LOC1 Client1/Client2
2 LOC2 Client3/Client4/Client5
3 LOC3 Client6
The desired output being
PickupPointCode LocationCode ClientCode
1 LOC1 Client1
1 LOC1 Client2
2 LOC2 Client3
2 LOC2 Client4
2 LOC2 Client5
3 LOC3 Client6
Using SQL serevr 2005 I wrote the below query to get this done
;WITH cte AS (
SELECT
PickupPointCode
,LocationCode
,CAST('<i>' + REPLACE(ClientCode, '/', '</i><i>') + '</i>' AS XML) AS ClientCodes
FROM <TABLE NAME>)
SELECT
PickupPointCode
,LocationCode
,x.i.value('.', 'VARCHAR(MAX)') AS ClientCode
FROM cte
CROSS APPLY ClientCodes.nodes('//i') x(i)
But now I have to do the same thing using SQL SERVER 2000. How can I perform the same?
Thanks
The basic idea is to multiply each row as many times as there are CLIENTs. Then, extract the appropriate part of the string.
Have a look at some example queries that work on SQL2005+ here.
You will need to work with an extra table that contains sequential numbers in SQL2000.
CREATE TABLE dbo.Numbers
(
N INT NOT NULL PRIMARY KEY
);
GO
DECLARE #rows AS INT;
SET #rows = 1;
INSERT INTO dbo.Numbers VALUES(1);
WHILE(#rows <= 1000)
BEGIN
INSERT INTO dbo.Numbers SELECT N + #rows FROM dbo.Numbers;
SET #rows = #rows * 2;
END
This is the numbers table you can join onto. And below is the query that should work on SQL 2000.
SELECT PickupPointCode, LocationCode,
SUBSTRING(ClientCode, Numbers.N,
CHARINDEX('/', ClientCode + '/', Numbers.N) - Numbers.N) AS ClientCode
FROM <TABLE_NAME>
JOIN Numbers ON Numbers.N <= DATALENGTH(ClientCode) + 1
AND SUBSTRING('/' + ClientCode, Numbers.N, 1) = '/'