SQL Index on changed column - sql

Is it possible to create an Index on column with changes of this column.
For example - I have a column A (nvarchar), But in query I should to replace values from this column to compare with values in list. Classical index will work only if I use original values from column A.
The query looks like next
SELECT
*
FROM
MyTable
WHERE
REPLACE(A, ' ', '') IN ('aasa', 'asa', 'wew','wewe')

You can create a computed column and then create an index on it.
Note: SQL Server Index key columns have a 900 byte size limit. Since your column is NVARCHAR, it consumes 2 bytes for each character. So, let's can cap the index at 400 characters (800 bytes).
To conserve space, we can further restrict this column to contain a value, only if it meets your required conditions.
ALTER TABLE MyTable
ADD A_NoSpace AS (CASE WHEN REPLACE(A, ' ', '') IN ('aasa', 'asa', 'wew','wewe') THEN LEFT(REPLACE(A, ' ', ''), 400) END) PERSISTED
CREATE NONCLUSTERED INDEX IX_A_NoSpace ON MyTable (A_NoSpace)
Being a PERSISTED column, calculation will take place only during INSERT or UPDATE operations.
You can now use this column in your query:
SELECT *
FROM
MyTable
-- Don't need to check if value is in the list,
-- because we're already doing this in the computed column definition
WHERE A_NoSpace IS NOT NULL

Related

INSERT INTO causing nulls in columns

I'm trying to concatenate two columns and add a comma and space between them in SQL Server 2014.
Here's the code I'm using (redacting the table name for privacy reasons)
INSERT INTO Table (EntityName)
SELECT CONCAT(MailingLine1, ', ', MailingState)
FROM Table
This is concatenating the strings correctly, but in the process, it's causing all other columns and all other rows to read as NULL and wipes the data from the table.
There are roughly 30 columns that already have data within them
Any thoughts?
If you are looking to add a new column with this function CONCAT(MailingLine1, ', ', MailingState) so you can use a computed column
--if [EnityName] already exists in the table you have to drop it first
alter table TABLE
drop column [EntityName]
alter table TABLE
add [EntityName] as CONCAT(MailingLine1, ', ', MailingState)
If you don't want to add a computed column you can follow #GordonLinoff answer (use UPDATE command)
Else, If you are looking to insert new rows not to update rows, the issue is that you are only inserting values in EntityName column and ignoring all other columns, you can assign values as the following (assuming you have 3 columns)
Insert into Table (EntityName,EntityName2,EntityName3)
SELECT CONCAT(MailingLine1, ', ', MailingState),Value1,Value2 FROM Table
Where Value1,Value2 can be columns from the table, or fixed value i.e. 'John'
Set a default value for the other volums, a not NULL value. (See example)
ALTER TABLE {table_name} ADD DEFAULT {dafault_value} FOR {column_name}
I'm guessing you want update, not insert:
update Table
set EntityName = CONCAT(MailingLine1, ', ', MailingState);

Duplicate a column into a temporary table then convert data

Is there a way to duplicate a column from a current database table (copy all the column contents from table to a temporary table), Then
Convert the string value in the column and increment it by 1, then
Put all those values in a form of a string back into it's original table?
So pseudocode would look like:
copy column1 from tblReal into tmpcolumn in tblTemp (set tmpcolumn1 as nvarchar(265))
update tblTemp
set tmpcolumn1 = 'TESTDATA' + 1
copy tbmpcolumn1 from tblTemp into column1 in tblReal
So actually you want to change a string column, which holds actually a number, by incrementing its value by 1. Why would you need three steps for that? Just do an update statement on the column immediatly. I don't see why you need intermediate tables.
UPDATE tblReal SET column1 = column1 + 1
Piece of cake. You can use the cast function to transform the varchar to a number and back again in the update statement.

SQL Server - Truncate Using DATALENGTH

Is there a way to truncate an nvarchar using DATALENGTH? I am trying to create an index on a column, but an index only accepts a maximum of 900 bytes. I have rows that consist of 1000+ bytes. I would like to truncate these rows and only accept the first n characters <= 900 bytes.
Can be this sql useful, Just update the table for that column.
Update Table
Set Column = Left(Ltrim(Column),900)
Create a COMPUTED COLUMN that represents the data you want to index, then create an index on it.
ALTER TABLE MyTable ADD ComputedColumn AS LEFT(LargeNVarcharColumn,900);
CREATE NONCLUSTERED INDEX MyIndex ON MyTable
(
ComputedColumn ASC
);
Reference:
computed_column_definition
Indexes on Computed Columns
Trim the column ,left or right side to 900 characters ,create a index on that column
ALTER TABLE usertable ADD used_column AS LEFT(nvarcharcolumn,900);
create a index on this used columm.it will work

SQL Identity with leading padded zeros

I have marked a column as Identity in my table
create table Identitytest(
number int identity(1,001) not null,
value varchar(500)
)
I need the identity column to be incremented as 001,002,003, etc.
The database shows that it is inserting as 1,2,3, etc.
How can this be done?
As the others have already rightfully pointed out - an INT never has leading zeroes - it just holds the value, that's all (and that's good that way).
If you need some additional formatting, you could always add a computed column to your table, something like:
ALTER TABLE dbo.Identitytest
ADD DisplayNumber AS RIGHT('000' + CAST(number AS VARCHAR(3)) , 3) PERSISTED
This way, your INT IDENTITY will be used as an INT and always contains the numerical value, while DisplayNumber contains 001, 002, ... 014, 015, ..... and so forth - automagically, always up to date.
Since it's a persisted field, it's now part of your table, and you can query on it, and even put an index on it to make queries faster:
SELECT value FROM dbo.IdentityTest WHERE DisplayNumber = '024'
And of course, you could use just about any formatting in the definition of your computed column, so you could also add a prefix or something:
ALTER TABLE dbo.Identitytest
ADD DisplayNumber
AS 'ABC-' + RIGHT('000' + CAST(number AS VARCHAR(3)) , 3) PERSISTED
So in this case, your DisplayNumber would be ABC-001, ABC-002, ... and so on.
You get the best of both worlds - you keep your INT IDENTITY which is numerical and automatically increased by SQL Server, and you can define a display format any way you like and have that available at any time.
If you want to display your number column with leading zeros, just pad it in your SELECT statement. It's a number, it will NOT store with leading zeros as an integer.
SELECT RIGHT('00000' + CAST([number] AS varchar(5)) , 3)
FROM IdentityTest
The 3 is the number of characters you want total in the output display.
If you require both the auto-incrementing number (which can only be a number) and an alphabetic representation of the number, you might consider looking at computed columns.
Here's a few links to get you going:
http://www.mssqltips.com/tip.asp?tip=1682
http://msdn.microsoft.com/en-us/library/ms191250.aspx
http://www.kodyaz.com/articles/sql-server-computed-column-calculated-column-sample.aspx
Why do you need that? As an integer, 001 is the same as 1. If what you want is that for display or other purposes, create another column and do your work there (you may do it as part of a trigger on the table, on insert, that looks at the newly inserted row, and creates the entry in the column appropriately.
i've got a table where i'm storing an integer, but the users want to see it a XXX, even if it has zeroes, so i wrote this code
declare #a int
set #a=1
select replicate('0',3-len(#a))+ cast(#a as varchar(4))
Here is another method:
create table TEST_T (ui int NOT NULL identity, name varchar(10))
insert into TEST_T values ( 'FRED' )
select NAME, ui, RIGHT('0000' + LTRIM(STR(ui)), 4) as ui_T from TEST_T
go
/* NOTE: A view could be created with a calculated column instead of the identity column. */
create view TEST_V as select NAME, RIGHT('0000' + LTRIM(STR(ui)), 4) as ui_V from TEST_T go
go
select * from TEST_V
drop view TEST_V
drop table TEST_T
Not quite as much data duplication(?) as adding a column to the table and no need to specify the column in the select statement.
I need the identity column to be
incremented as 001,002,003, etc.
The database shows that it is
inserting as 1,2,3, etc.
SQL databases store values, not the literals you used to write those values. 002 is 2. Just like 1 + 1 is 2. Would you expect SELECT 1 + 1 to display the string "1 + 1" instead of 2?
If you want the leading zeros to be stored in your column, you have to use a character type. But then you can't use AUTOINCREMENT/IDENTITY.
What you probably really want is something like printf("%03d", number) in program that reads from the database.

Getting all rows from a Table where the column contains only 0

I got a little problem
i need a sql query that gives all rows back that only contains 0 in it.
the column is defined as varchar2(6)
the values in the column looks like this:
Row Value
1 0
2 00
3 00
4 100
5 bc00
6 000000
7 00000
my first solution would be like this:
Oracle:
substr('000000' || COLUMN_NAME, -6) = '000000'
SQL Server:
right('000000' + COLUMN_NAME, 6) = '000000'
is there an other way?
(it needs to work on both systems)
the output would be the row 1,2,3,6,7
This is the simplest one:
select * from tbl where replace(col,'0','') = ''
If you will not make computed column for that expression, you can opt for function-based index(note: Oracle and Postgres already supports this; Sql Server as of version 2008, not yet) to make that performant:
create index ix_tbl on tbl(replace(col,'0',''))
[EDIT]
I just keep the answer below for posterity, I tried to explain how to make the query use index from computed column.
Use this:
select * from tbl
where ISNUMERIC(col) = 1 and cast(col as int) = 0
For ISNUMERIC needs on Oracle, use this: http://www.oracle.com/technology/oramag/oracle/04-jul/o44asktom.html
[EDIT]
#Charles, re: computed column on Oracle:
For RDBMSes that supports computed column but it doesn't have persisted option, yes it will make function call for every row. If it supports persisted column, it won't make function call, you have real column on the table which is precomputed from that function. Now, if the data could make the function raise an exception, there are two scenarios.
First, if you didn't specify persist, it will allow you to save the computed column (ALTER TABLE tbl ADD numeric_equivalent AS cast(col as int)) even if the result from the data will raise an exception, but you cannot unconditionally select that column, this will raise exception:
select * from tbl
this won't raise exception:
select * from tbl where is_col_numeric = 1
this will:
select * from tbl where numeric_equivalent = 0 and is_col_numeric = 1
this won't (Sql Server supports short-circuiting):
select * from tbl where is_col_numeric = 1 and numeric_equivalent = 0
For reference, the is_col_numeric above was created using this:
ALTER TABLE tbl ADD
is_col_numeric AS isnumeric(col)
And this is is_col_numeric's index:
create index ix_is_col_numeric on tbl(is_col_numeric)
Now for the second scenario, you put computed column with PERSISTED option on table that already has existing data(e.g. 'ABXY','X1','ETC') that raises exception when function/expression(e.g. cast) is applied to it, your RDBMS will not allow you to make a computed column. If your table has no data, it will allow you to put PERSISTED option, but afterwards when you attempt to insert data(e.g. insert into tbl(col) values('ABXY')) that raises an exception, your RDBMS will not allow you to save your data. Thereby only numeric text can be saved in your table, your PERSISTED computed column degenerate into a constraint check, albeit a full detoured one.
For reference, here's the persisted computed column sample:
ALTER TABLE tbl ADD
numeric_equivalent AS cast(col as int) persisted
Now, some of us might be tempted to not put PERSISTED option on computed column. This would be kind of self-defeating endeavor in terms of performance purposes, because you might not be able to create index on them later. When later you want to create index on the unpersisted computed column, and the table already has data 'ABXY', the database won't allow you to create an index. Index creation need to obtain the value from column, and if that column raises an exception, it won't allow you to create index on it.
If we attempt to cheat a bit i.e. we immediately create an index on that unpersisted computed column upon table creation, the database will allow you to do that. But when we insert 'ABXY' to table later, it will not be saved, the database is automatically constructing index(es) after we insert data to the table. The index constructor receives exception instead of data, so it cannot make an index entry for the data we tried inserting, subsequently inserting data will not happen.
So how can we attain index nirvana on computed column? First of all, we make sure that the computed column is PERSISTED, doing this will ensure that errors kicks-in immediately; if we don't put PERSISTED option, anything that could raise exception will be deferred to index construction, just making things fail later. Bugs are easier to find when they happen sooner. After making the column persisted, put an index on it
So if we have existing data '00','01', '2', this will allow us to make persisted computed column. Now after that, if we insert 'ABXY', it will not be inserted, the database cannot persist anything from computed column that raised an exception. So we will just roll our own cast that doesn't raise exception.
To wit(just translate this to Oracle equivalent):
create function cast_as_int(#n varchar(20)) returns int with schemabinding
begin
begin try
return cast(#n as int);
end try
begin catch
return null;
end catch
end;
Please do note that catching exception in UDF will not work yet in Sql Server, but Microsoft have plans to support that
This is now our non-exception-raising persisted computed column:
ALTER TABLE tbl ADD
numeric_equivalent AS cast_as_int(a) persisted
Drop the existing index, then recreate it:
create index ix_num_equiv on tbl(numeric_equivalent)
Now this query will become index-abiding-citizen, performant, and won't raise exception even the order of conditions is reversed:
select * from tbl where numeric_equivalent = 0 and is_col_numeric = 1
To make it more performant, since the numeric_equivalent column doesn't raise any more exceptions, we have no more use for is_col_numeric, so just use this:
select * from tbl where numeric_equivalent = 0
Do you like:
SELECT * FROM MY_TABLE
WHERE REPLACE (MY_COLUMN, '0', NULL) IS NULL
AND MY_COLUMN IS NOT NULL;
This would also work in Oracle (but not in SQL Server):
REPLACE(column_name, '0') IS NULL
This will work in Oracle (and perhaps also in SQL Server, you will have to check):
LTRIM(column_name, '0') IS NULL
Alternatively, since it is a VARCHAR(6) column, you could also just check:
column_name IN ('0', '00', '000', '0000', '00000', '000000')
This is not pretty but it is probably the most efficient if there is an index on the column.
Building off KM's answer, you can do the same thing in Oracle without needing to create an actual table.
SELECT y.*
FROM YourTable y
WHERE YourColumn IN
(SELECT LPAD('0',level,'0') FROM dual CONNECT BY LEVEL <= 6)
or
SELECT y.*
FROM YourTable y
INNER JOIN
(SELECT LPAD('0',level,'0') zeros FROM dual CONNECT BY LEVEL <= 6) z
ON y.YourColumn = z.zeros
I think this is the most flexible answer because if the maximum length of the column changes, you just need to change 6 to the new length.
How about using regular expression (supported by oracle, I think also MSSQL)
Another SQL version would be:
...
where len(COLUMN_NAME) > 0
and len(replace(COLUMN_NAME, '0', '')) = 0
i.e., where there are more than 1 characters in the column, and all of them are 0. Toss in TRIM if there can be leading, trailing, or embedded spaces.
try this, which should be able to use and index on YourTable.COLUMN_NAME if it exists:
--SQL Server syntax, but should be similar in Oracle
--you could make this a temp of permanent table
CREATE TABLE Zeros (Zero varchar(6))
INSERT INTO Zeros VALUES ('0')
INSERT INTO Zeros VALUES ('00')
INSERT INTO Zeros VALUES ('000')
INSERT INTO Zeros VALUES ('0000')
INSERT INTO Zeros VALUES ('00000')
INSERT INTO Zeros VALUES ('000000')
SELECT
y.*
FROM YourTable y
INNER JOIN Zeros z On y.COLUMN_NAME=z.Zero
EDIT
or even just this:
SELECT
*
FROM YourTable
WHERE COLUMN_NAME IN ('0','00','000','0000','00000','000000')
building off of Dave Costa's answer:
Oracle:
SELECT
*
FROM YourTable
WHERE YourColumn IN
(SELECT LPAD('0',level,'0') FROM dual CONNECT BY LEVEL <= 6)
SQL Server 2005 and up:
;WITH Zeros AS
(SELECT
CONVERT(varchar(6),'0') AS Zero
UNION ALL
SELECT '0'+CONVERT(varchar(5),Zero)
FROM Zeros
WHERE LEN(CONVERT(varchar(6),Zero))<6
)
select Zero from Zeros
SELECT
y.*
FROM YourTable y
WHERE y.COLUMN_NAME IN (SELECT Zero FROM Zeros)