T-SQL Identity Seed Expression [closed] - sql

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
Is it possible to use an expression for identity seeds in T-SQL?
We're putting a bunch of tables together, using BIGINT for the ID columns, but want to stagger the seeds so that the tables are not overlapping their ID number spaces.
This is much easier to do using hex values, so we can mask out the highest two bytes or so, such as seed 1 * 0x1000000000000 for table1 and seed 2 * 0x1000000000000 for table2 etc. - thereby still leaving plenty of possible IDs available for each table.
The problem here is, SQL doesn't like seeing the hex values or multiplication in the IDENTITY statement, so we tried manually casting them to BIGINT but the error persisted: "incorrect syntax, expected INTEGER or NUMERIC."
It seems like T-SQL doesn't want to see anything other than a single literal value, in decimal (not hex), with no math operations.
We can deal with this, by doing the math ourselves and converting the numbers to decimal - but we'd like to avoid this if possible, since the numbers are more difficult to keep track of in decimal format - bug prone, etc.
(I should explain, bug-prone, because we use these values to determine which table an object belongs to, based solely on it's ID value being in the appropriate number space - those first two bytes being a sort of "table ID")
However, is there another way to accomplish what I'm describing using hex values and multiplication, while using some weird syntax that T-SQL can accept?
I know this is an inconvenience, not a blocking issue, but I want to make sure there truly aren't any alternatives before we settle on this workaround.

Just blend bad ideas by using dynamic SQL:
declare #Bar as BigInt = 0x1000000000000;
declare #Foo as NVarChar(1000) = 'dbcc checkident(''Foo'', RESEED, ' + cast( 2 * #Bar as NVarChar(64) ) + ')';
exec sp_executesql #Foo;
I'm sure a RegEx would improve it.

Create a trigger (before insert) for this table, and disable identity.
Convert Hex to int: Convert integer to hex and hex to integer
Example:
CREATE TRIGGER TRIGGER_NAME
ON TABLE_NAME
INSTEAD OF INSERT
AS
BEGIN
IF (SELECT ID FROM INSERTED) IS NULL
BEGIN
DECLARE #INITIAL_ID BIGINT = (SELECT CONVERT(BIGINT, 0x1000000000000) * 2)
DECLARE #NEW_ID BIGINT =
ISNULL( (SELECT MAX(ID) FROM TABLE_NAME) + 1, #INITIAL_ID )
SELECT * INTO #INSERTED FROM INSERTED
UPDATE #INSERTED SET ID = #NEW_ID
INSERT INTO TABLE_NAME SELECT * FROM #INSERTED
END
ELSE
BEGIN
INSERT INTO TABLE_NAME SELECT * FROM INSERTED
END
END
GO

When you create the table you can set the initial seed value.
create table Table1(Id int Identity(10000000,1), Name varchar(255));
or you can use this statement on a table that is already created
DBCC CHECKIDENT ('dbo.Table1', RESEED, 20000000);
The next entry will be 20000001 assuming you have the step = 1

Related

How to DECLARE variable and set values to a field in a table [duplicate]

This question already has answers here:
SQL Server store multiple values in sql variable
(7 answers)
Closed 2 months ago.
I am trying to set values but need to use a field, rather than inputting hundreds of values.
Current code:
DECLARE #variable AS VARCHAR (100)
SET #variable = 'Y'
I need to be able to use a field as the value:
SET #variable = tbl.field
I have also tried
DECLARE #variable AS table (val varchar (100))
insert into #variable (val)
(SELECT
distinct field
FROM
tbl])
select * from #variable
SELECT * FROM tbl
WHERE field = #variable
However this code simply runs both at the same time, creating two outputs, so I am missing a link here
I need to be able to run the code so that all available values are set as each option needs to be tested at once.
You declared a scalar variable. It holds only one value, and cannot hold more than one value.
In this approach, you can store multiple values.
DECLARE #variable AS TABLE(val VARCHAR(50))
INSERT INTO #variable(val) VALUES
('Y'),
('N')
SELECT * FROM #variable
As Larnu wrote, you can't assign a scalar value to hold two values.
My suggestions are using a temporary table to hold all of your values, or assign both values as one and break them with STRING_SPLIT.
For example:
DECLARE #variable VARCHAR(100) = 'Y,N'
SELECT value
FROM STRING_SPLIT(#variable, ',')
A scaler variable only can store a single value in each set/select statement.

How to convert or cast int to string in SQL Server

Looking at a column that holds last 4 of someone's SSN and the column was originally created as an int datatype. Now SSN that begin with 0 get registered as 0 on the database.
How can I convert the column and it's information from an int into a string for future proof?
You should convert. CONVERT(VARCHAR(4), your_col)
If you specifically want zero-padded numbers, then the simplest solution is format():
select format(123, '0000')
If you want to fix the table, then do:
alter table t alter column ssn4 char(4); -- there are always four digits
Then update the value to get the leading zeros:
update t
ssn4 = format(convert(int, ssn4), '0000');
Or, if you just want downstream users to have the string, you can use a computed column:
alter table t
add ssn4_str as (format(ssn4, '0000'));
If you want to add leading zeros, use:
SELECT RIGHT('0000'+ISNULL(SSN,''),4)
First thing never store SSN or Zip Code as any numeric type.
Second you should fix the underlying table structure not rely on a conversion...but if you're in a jam this is an example of a case statement that will help you.
IF OBJECT_ID('tempdb..#t') IS NOT NULL
BEGIN
DROP TABLE #t
END
GO
CREATE TABLE #t(
LastFourSSN INT
)
INSERT INTO #t(LastFourSSN)
VALUES('0123'),('1234')
SELECT LastFourSSN --strips leading zero
FROM #t
SELECT -- adds leading zero to anything less than four charaters
CASE
WHEN LEN(LastFourSSN) < 4
THEN '0' + CAST(LastFourSSN AS VARCHAR(3))
ELSE CAST(LastFourSSN AS VARCHAR(4))
END LastFourSSN
FROM #t
If you are looking for converting values in the column for your purpose to use in application, you can use this following-
SELECT CAST(your_column AS VARCHAR(100))
--VARCHAR length based on your data
But if you are looking for change data type of your database column directly, you can try this-
ALTER TABLE TableName
ALTER COLUMN your_column VARCHAR(200) NULL
--NULL or NOT NULL based on the data already stored in database

Setting a variable with multiple rows in SSIS [duplicate]

This question already has answers here:
Which is the best way to form the string value using column from a Table with rows having same ID?
(2 answers)
Closed 7 years ago.
I need to do something like this in SSIS:
From one SQL table I need to get some id values, I am using a simple sql query:
Select ID from Identifier where value is not null.
I've got this result:
As a final result I need to generate and set a variable in SSIS with the final value:
#var = '198','120','ACP','120','PQU'
Which I need to use later in a odbc expression.
Is this possible in SSIS?
Just to clarify: The image is just a little example of what I can get from the first part of the process. I mean, the number of ID that I need is unknown.
Use simple query
DECLARE #TEST TABLE(ID NVARCHAR(10))
INSERT INTO #TEST VALUES('186'), ('120'), ('ACP'), ('120'), ('PQU')
DECLARE #ID VARCHAR(8000)
SELECT #ID = CONCAT(COALESCE(#ID + ',''', ''''), ID, '''')
FROM #TEST
SELECT #ID
Result
'186','120','ACP','120','PQU'

Why upsert is not a fundamental SQL operation [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
Why don't SQL support the upsert use case? I am not asking how to do in a particular db(*). What I want to know is why upsert is not a fundamental operation like insert and update. I mean it is a pretty straightforward use case right? I guess there must be some fundamental db first principle that gets broken when doing upsert or some technical challenge that the db engine faces when confronted with a upsert.
*. I know about the mysql syntax and the SQL Merge syntax. BTW even while using such db specific syntax you need to be careful about atomicity and locking. And using the merge syntax, it doesn't feel right having to create a psuedo table.
Edit: I am editing this to clarify that I am not asking an opinion. So I dont think this question should be blocked.
Because it isn't easily handable, both acid and syntax-wise.
The conditions for update if exists isn't clear.
For example, replace "insert into" with upsert in the below query
insert into t_something
select * from t_whatever
No foreign keys, no primary keys.
How do you want to update ?
Would the where condition be for the select, or for the update ?
Ultimately, you have to write the condition, and then you can just as well do a "update/insert if"...
Usually, when you're asking yourself the upsert question, you're handling inserting/updating wrong.
You're thinking in object terms instead of set terms.
You want to loop through an array of objects, and insert if count(*) on exists is 0 else update.
That's how object-oriented imperative programming works, but that's not how SQL works.
In SQL, you operate with a SET.
You can easily do a inner join - update on the SET
and a left join where null insert on the same SET.
That's just as comfortable as a merge, and a lot more readable plus simpler to debug.
And it might well be faster.
You can already ensure it's all atomic by putting update & insert into a transaction.
Thinking of upsert, which idiotism do you want next ? "UpSertLeteTrunc" ? MerDel ?
Or perhaps truncsert ?
There are more important things to do, by far.
This is how I do Upsert with MERGE on SQL-Server:
-- How to create the XML
/*
DECLARE #xml XML
SET #xml = ( SELECT (SELECT * FROM T_Benutzer FOR XML PATH('row'), ROOT('table'), ELEMENTS xsinil) AS outerXml )
-- SELECT #xml
*/
DECLARE #xml xml
SET #xml = '<table xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<row>
<PLK_UID>7CA68E6E-E998-FF92-BE70-126064765EAB</PLK_UID>
<PLK_Code>A2 Hoch</PLK_Code>
<PLK_PS_UID>6CF3B5AB-C6C8-4A12-8717-285F95A1084B</PLK_PS_UID>
<PLK_DAR_UID xsi:nil="true" />
<PLK_Name_DE>Mit Legende</PLK_Name_DE>
<PLK_Name_FR>Avec Légende</PLK_Name_FR>
<PLK_Name_IT>Con Leggenda</PLK_Name_IT>
<PLK_Name_EN>With Legend</PLK_Name_EN>
<PLK_IsDefault>0</PLK_IsDefault>
<PLK_Status>1</PLK_Status>
</row>
</table>'
DECLARE #handle INT
DECLARE #PrepareXmlStatus INT
EXEC #PrepareXmlStatus = sp_xml_preparedocument #handle OUTPUT, #XML
;WITH CTE AS
(
SELECT
PLK_UID
,PLK_Code
,PLK_PS_UID
,PLK_DAR_UID
,PLK_Name_DE
,PLK_Name_FR
,PLK_Name_IT
,PLK_Name_EN
,PLK_IsDefault
,PLK_Status
FROM OPENXML(#handle, '/table/row', 2) WITH
(
"PLK_UID" uniqueidentifier 'PLK_UID[not(#*[local-name()="nil" and . ="true"])]'
,"PLK_Code" character varying(10) 'PLK_Code[not(#*[local-name()="nil" and . ="true"])]'
,"PLK_PS_UID" uniqueidentifier 'PLK_PS_UID[not(#*[local-name()="nil" and . ="true"])]'
,"PLK_DAR_UID" uniqueidentifier 'PLK_DAR_UID[not(#*[local-name()="nil" and . ="true"])]'
,"PLK_Name_DE" national character varying(255) 'PLK_Name_DE[not(#*[local-name()="nil" and . ="true"])]'
,"PLK_Name_FR" national character varying(255) 'PLK_Name_FR[not(#*[local-name()="nil" and . ="true"])]'
,"PLK_Name_IT" national character varying(255) 'PLK_Name_IT[not(#*[local-name()="nil" and . ="true"])]'
,"PLK_Name_EN" national character varying(255) 'PLK_Name_EN[not(#*[local-name()="nil" and . ="true"])]'
,"PLK_IsDefault" bit 'PLK_IsDefault[not(#*[local-name()="nil" and . ="true"])]'
,"PLK_Status" int 'PLK_Status[not(#*[local-name()="nil" and . ="true"])]'
) AS tSource
WHERE (1=1)
-- AND NOT EXISTS
-- (
-- SELECT * FROM T_VWS_Ref_PdfLegendenKategorie
-- WHERE T_VWS_Ref_PdfLegendenKategorie.PLK_UID = tSource.PLK_UID
--)
)
-- SELECT * FROM CTE
MERGE INTO T_VWS_Ref_PdfLegendenKategorie AS A
USING CTE ON CTE.PLK_UID = A.PLK_UID
WHEN MATCHED
THEN UPDATE
SET A.PLK_Code = CTE.PLK_Code
,A.PLK_PS_UID = CTE.PLK_PS_UID
,A.PLK_DAR_UID = CTE.PLK_DAR_UID
,A.PLK_Name_DE = CTE.PLK_Name_DE
,A.PLK_Name_FR = CTE.PLK_Name_FR
,A.PLK_Name_IT = CTE.PLK_Name_IT
,A.PLK_Name_EN = CTE.PLK_Name_EN
,A.PLK_IsDefault = CTE.PLK_IsDefault
,A.PLK_Status = CTE.PLK_Status
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
PLK_UID
,PLK_Code
,PLK_PS_UID
,PLK_DAR_UID
,PLK_Name_DE
,PLK_Name_FR
,PLK_Name_IT
,PLK_Name_EN
,PLK_IsDefault
,PLK_Status
)
VALUES
(
CTE.PLK_UID
,CTE.PLK_Code
,CTE.PLK_PS_UID
,CTE.PLK_DAR_UID
,CTE.PLK_Name_DE
,CTE.PLK_Name_FR
,CTE.PLK_Name_IT
,CTE.PLK_Name_EN
,CTE.PLK_IsDefault
,CTE.PLK_Status
)
-- WHEN NOT MATCHED BY SOURCE THEN DELETE
;
EXEC sp_xml_removedocument #handle

SQL Server 2008 query to find rows containing non-alphanumeric characters in a column

I was actually asked this myself a few weeks ago, whereas I know exactly how to do this with a SP or UDF but I was wondering if there was a quick and easy way of doing this without these methods. I'm assuming that there is and I just can't find it.
A point I need to make is that although we know what characters are allowed (a-z, A-Z, 0-9) we don't want to specify what is not allowed (##!$ etc...). Also, we want to pull the rows which have the illegal characters so that it can be listed to the user to fix (as we have no control over the input process we can't do anything at that point).
I have looked through SO and Google previously, but was unable to find anything that did what I wanted. I have seen many examples which can tell you if it contains alphanumeric characters, or doesn't, but something that is able to pull out an apostrophe in a sentence I have not found in query form.
Please note also that values can be null or '' (empty) in this varchar column.
Won't this do it?
SELECT * FROM TABLE
WHERE COLUMN_NAME LIKE '%[^a-zA-Z0-9]%'
Setup
use tempdb
create table mytable ( mycol varchar(40) NULL)
insert into mytable VALUES ('abcd')
insert into mytable VALUES ('ABCD')
insert into mytable VALUES ('1234')
insert into mytable VALUES ('efg%^&hji')
insert into mytable VALUES (NULL)
insert into mytable VALUES ('')
insert into mytable VALUES ('apostrophe '' in a sentence')
SELECT * FROM mytable
WHERE mycol LIKE '%[^a-zA-Z0-9]%'
drop table mytable
Results
mycol
----------------------------------------
efg%^&hji
apostrophe ' in a sentence
Sql server has very limited Regex support. You can use PATINDEX with something like this
PATINDEX('%[a-zA-Z0-9]%',Col)
Have a look at PATINDEX (Transact-SQL)
and Pattern Matching in Search Conditions
I found this page with quite a neat solution. What makes it great is that you get an indication of what the character is and where it is. Then it gives a super simple way to fix it (which can be combined and built into a piece of driver code to scale up it's application).
DECLARE #tablename VARCHAR(1000) ='Schema.Table'
DECLARE #columnname VARCHAR(100)='ColumnName'
DECLARE #counter INT = 0
DECLARE #sql VARCHAR(MAX)
WHILE #counter <=255
BEGIN
SET #sql=
'SELECT TOP 10 '+#columnname+','+CAST(#counter AS VARCHAR(3))+' as CharacterSet, CHARINDEX(CHAR('+CAST(#counter AS VARCHAR(3))+'),'+#columnname+') as LocationOfChar
FROM '+#tablename+'
WHERE CHARINDEX(CHAR('+CAST(#counter AS VARCHAR(3))+'),'+#columnname+') <> 0'
PRINT (#sql)
EXEC (#sql)
SET #counter = #counter + 1
END
and then...
UPDATE Schema.Table
SET ColumnName= REPLACE(Columnname,CHAR(13),'')
Credit to Ayman El-Ghazali.
SELECT * FROM TABLE_NAME WHERE COL_NAME LIKE '%[^0-9a-zA-Z $#$.$-$''''$,]%'
This works best for me when I'm trying to find any special characters in a string