Here's my table:
id name
1 ទឹក កាបូន
2 លីអូ បៀរ
3 ស្របៀរ ២៤
4 ស្រាបៀរ ឌ្រាប់
When I query using this statement: SELECT * FROM t1 WHERE name = N'លីអូ បៀរ', it returns all rows. This is weird since those Thai characters are different.
Also, this will cause an issue if I make the name column as unique. Does anyone encounter the same issue and what is the possible fix? I tried changing the collation but still no avail.
How to make SQL server recognize unique Thai characters?
Bing translate identifies those characters as Khmer, not Thai. So you need to pick a collation that has language-specific rules for those characters, eg
drop table if exists t1
create table t1(id int, name nvarchar(200) collate Khmer_100_CI_AI )
insert into t1(id,name)
values (1, N'ទឹក កាបូន'),(2, N'លីអូ បៀរ'),(3, N'ស្របៀរ ២៤'),(4, N'ស្រាបៀរ ឌ្រាប់')
SELECT * FROM t1 WHERE name = N'លីអូ បៀរ'
Or use binary collation, that simply compares the characters by their code point values. eg
drop table if exists t1
create table t1(id int, name nvarchar(200) collate Latin1_General_100_BIN2 )
insert into t1(id,name)
values (1, N'ទឹក កាបូន'),(2, N'លីអូ បៀរ'),(3, N'ស្របៀរ ២៤'),(4, N'ស្រាបៀរ ឌ្រាប់')
SELECT * FROM t1 WHERE name = N'លីអូ បៀរ'
Even some of the newer Latin collations will work, eg
drop table if exists t1
create table t1(id int, name nvarchar(200) collate Latin1_General_100_CI_AI )
insert into t1(id,name)
values (1, N'ទឹក កាបូន'),(2, N'លីអូ បៀរ'),(3, N'ស្របៀរ ២៤'),(4, N'ស្រាបៀរ ឌ្រាប់')
SELECT * FROM t1 WHERE name = N'លីអូ បៀរ'
Related
Yesterday suddenly a report occurred that someone was not able to get some data anymore because the issue Msg 2628, Level 16, State 1, Line 57 String or binary data would be truncated in table 'tempdb.dbo.#BC6D141E', column 'string_2'. Truncated value: '!012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678'. appeared.
I was unable to create a repro without our tables. This is the closest as I can get to:
-- Create temporary table for results
DECLARE #results TABLE (
string_1 nvarchar(100) NOT NULL,
string_2 nvarchar(100) NOT NULL
);
CREATE TABLE #table (
T_ID BIGINT NULL,
T_STRING NVARCHAR(1000) NOT NULL
);
INSERT INTO #table VALUES
(NULL, '0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789'),
(NULL, '!0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789!');
WITH abc AS
(
SELECT
'' AS STRING_1,
t.T_STRING AS STRING_2
FROM
UT
INNER JOIN UTT ON UTT.UT_ID = UT.UT_ID
INNER JOIN MV ON MV.UTT_ID = UTT.UTT_ID
INNER JOIN OT ON OT.OT_ID = MV.OT_ID
INNER JOIN #table AS T ON T.T_ID = OT.T_ID -- this will never get hit because T_ID of #table is NULL
)
INSERT INTO #results
SELECT STRING_1, STRING_2 FROM abc
ORDER BY LEN(STRING_2) DESC
DROP TABLE #table;
As you can see the join of #table cannot yield any results because all T_ID are NULL nevertheless I am getting the error mentioned above. The result set is empty.
That would be okay if a text with more than 100 characters would be in the result set but that is not the case because it is empty. If I remove the INSERT INTO #results and display the results it does not contain any text with more than 100 characters. The ORDER BY was only used to determine the faulty text value (with the original data).
When I use SELECT STRING_1, LEFT(STRING_2, 100) FROM abc it does work but it does not contain the text either that is meant to be truncated.
Therefore: What am I missing? Is it a bug of SQL Server?
-- this will never get hit is a bad assumption. It is well known and documented that SQL Server may try to evaluate parts of your query before it's obvious that the result is impossible.
A much simpler repro (from this post and this db<>fiddle):
CREATE TABLE dbo.t1(id int NOT NULL, s varchar(5) NOT NULL);
CREATE TABLE dbo.t2(id int NOT NULL);
INSERT dbo.t1 (id, s) VALUES (1, 'l=3'), (2, 'len=5'), (3, 'l=3');
INSERT dbo.t2 (id) VALUES (1), (3), (4), (5);
GO
DECLARE #t table(dest varchar(3) NOT NULL);
INSERT #t(dest) SELECT t1.s
FROM dbo.t1
INNER JOIN dbo.t2 ON t1.id = t2.id;
Result:
Msg 2628, Level 16, State 1
String or binary data would be truncated in table 'tempdb.dbo.#AC65D70E', column 'dest'. Truncated value: 'len'.
While we should have only retrieved rows with values that fit in the destination column (id is 1 or 3, since those are the only two rows that match the join criteria), the error message indicates that the row where id is 2 was also returned, even though we know it couldn't possibly have been.
Here's the estimated plan:
This shows that SQL Server expected to convert all of the values in t1 before the filter eliminated the longer ones. And it's very difficult to predict or control when SQL Server will process your query in an order you don't expect - you can try with query hints that attempt to either force order or to stay away from hash joins but those can cause other, more severe problems later.
The best fix is to size the temp table to match the source (in other words, make it large enough to fit any value from the source). The blog post and db<>fiddle explain some other ways to work around the issue, but declaring columns to be wide enough is the simplest and least intrusive.
I have text stored in the table "StructureStrings"
Create Table StructureStrings(Id INT Primary Key,String nvarchar(4000))
Sample Data:
Id String
1 Select * from Employee where Id BETWEEN ### and ### and Customer Id> ###
2 Select * from Customer where Id BETWEEN ### and ###
3 Select * from Department where Id=###
and I want to replace the "###" word with a values fetched from another table
named "StructureValues"
Create Table StructureValues (Id INT Primary Key,Value nvarcrhar(255))
Id Value
1 33
2 20
3 44
I want to replace the "###" token present in the strings like
Select * from Employee where Id BETWEEN 33 and 20 and Customer Id> 44
Select * from Customer where Id BETWEEN 33 and 20
Select * from Department where Id=33
PS: 1. Here an assumption is that the values will be replaced with the tokens in the same order i.e first occurence of "###" will be replaced by first value of
"StructureValues.Value" column and so on.
Posting this as a new answer, rather than editting my previous.
This uses Jeff Moden's DelimitedSplit8K; it does not use the built in splitter available in SQL Server 2016 onwards, as it does not provide an item number (thus no join criteria).
You'll need to firstly put the function on your server, then you'll be able to use this. DO NOT expect it to perform well. There's a lot of REPLACE in this, which will hinder performance.
SELECT (SELECT REPLACE(DS.Item, '###', CONVERT(nvarchar(100), SV.[Value]))
FROM StructureStrings sq
CROSS APPLY DelimitedSplit8K (REPLACE(sq.String,'###','###|'), '|') DS --NOTE this uses a varchar, not an nvarchar, you may need to change this if you really have Unicode characters
JOIN StructureValues SV ON DS.ItemNumber = SV.Id
WHERE SS.Id = sq.id
FOR XML PATH ('')) AS NewString
FROM StructureStrings SS;
If you have any question, please place the comments on this answer; do not put them under the question which has already become quite a long discussion.
Maybe this is what you are looking for.
DECLARE #Employee TABLE (Id int)
DECLARE #StructureValues TABLE (Id int, Value int)
INSERT INTO #Employee
VALUES (1), (2), (3), (10), (15), (20), (21)
INSERT INTO #StructureValues
VALUES (1, 10), (2, 20)
SELECT *
FROM #Employee
WHERE Id BETWEEN (SELECT MIN(Value) FROM #StructureValues) AND (SELECT MAX(Value) FROM #StructureValues)
Very different take here:
CREATE TABLE StructureStrings(Id int PRIMARY KEY,String nvarchar(4000));
INSERT INTO StructureStrings
VALUES (1,'SELECT * FROM Employee WHERE Id BETWEEN ### AND ###'),
(2,'SELECT * FROM Customer WHERE Id BETWEEN ### AND ###');
CREATE TABLE StructureValues (Id int, [Value] int);
INSERT INTO StructureValues
VALUES (1,10),
(2,20);
GO
DECLARE #SQL nvarchar(4000);
--I'm asuming that as you gave one output you are supplying an ID or something?
DECLARE #Id int = 1;
WITH CTE AS(
SELECT SS.Id,
SS.String,
SV.[Value],
LEAD([Value]) OVER (ORDER BY SV.Id) AS NextValue,
STUFF(SS.String,PATINDEX('%###%',SS.String),3,CONVERT(varchar(10),[Value])) AS ReplacedString
FROM StructureStrings SS
JOIN StructureValues SV ON SS.Id = SV.Id)
SELECT #SQL = STUFF(ReplacedString,PATINDEX('%###%',ReplacedString),3,CONVERT(varchar(10),NextValue))
FROM CTE
WHERE Id = #Id;
PRINT #SQL;
--EXEC (#SQL); --yes, I should really be using sp_executesql
GO
DROP TABLE StructureValues;
DROP TABLE StructureStrings;
Edit: Note that Id 2 will return NULL, as there isn't a value to LEAD to. If this needs to change, we'll need more logic on what the value should be if there is not value to LEAD to.
Edit 2: This was based on the OP's original post, not what he puts it as later. As it currently stands, it's impossible.
I have a table with a xml column.
I require to search for sub string in that xml column for all its node and value. Search should be case insensitive
Structure of XML in each row is different
I used below query to do that,
select * from TableName Where Cast(xmlcolumn as varchar(max) ) like '%searchString%'
this works for short length xml rows, if row length goes huge it cant handle the situation. Only partial of the data was searched.
Suggest me some other ways to achieve.
If this is one time task then I would use exist XML method thus:
DECLARE #Table1 TABLE (
ID INT IDENTITY PRIMARY KEY,
CommentAsXML XML
)
INSERT #Table1 (CommentAsXML)
VALUES (N'<root><item /><item type="Reg">0001</item><item type="Inv">B007</item><item type="Cus">A0001</item><item type="Br">F0001</item></root>')
INSERT #Table1 (CommentAsXML)
VALUES (N'<root><item /><item type="Reg">0005</item><parent><child>B007</child></parent><item type="Br">F0005</item></root>')
INSERT #Table1 (CommentAsXML)
VALUES (N'<root><item /><item type="Reg">0005</item></root>')
-- Following query is searching for B007 within InnerText of all XML elements:
SELECT *
FROM #Table1 t
WHERE t.CommentAsXML.exist('//*[lower-case(text()[1]) eq "b007"]') = 1
Results:
ID CommentAsXML
-- ------------------------------------------------------------------------------------------------------------------------------
1 <root><item type="Reg">0001</item><item type="Inv">B007</item><item type="Cus">A0001</item><item type="Br">F0001</item></root>
2 <root><item type="Reg">0005</item><parent><child>B007</child></parent><item type="Br">F0005</item></root>
Also, if you want to search for some text in XML atrributes' values then following XQuery could be used:
SELECT *
FROM #Table1 t
WHERE t.CommentAsXML.exist('//#*[lower-case(.) eq "reg"]') = 1
Note: in both cases, string constants (ex. "reg") should be with lower cases.
I use query to select name with all small letters but he gets all names with small letters and upper letter
this is what I used
select Name from _Client where Name = 'mrmiro'
I got 2 names with small and upper letters like this
mrmiro
MrMiro
I just want to select exactly what I searched
Small matter of changing COLLATE in your query
Declare #Table table (Name varchar(25))
Insert Into #Table values
('mrmiro'),
('MrMiro')
Select *
From #Table
Where Name = 'mrmiro' COLLATE SQL_Latin1_General_CP1_CS_AS
Returns
Name
mrmiro
Here's my setup: I am passing two arrays as XML into a SQL stored procedure.
These are:
<PhoneID Value=128/>
<PhoneID Value=129/>
<PhoneID Value=130/>
and
<AddressID Value=268/>
<AddressID Value=157/>
<AddressID Value=395/>
The Address and Phone tables look like this (pseudo-code follows):
Phone:
BIGINT PhoneID
BIGINT PhoneNumber
SMALLINT AreaCode
INT Extension
Address:
BIGINT AddressID
NVARCHAR StreetAddress
NVARCHAR CountryName
NVARCHAR City
BIGINT Zip
My dilemma is this:
I need to walk through the passed in arrays in lock-step to return
ContactInfo:
BIGINT PhoneNumber
NVARCHAR StreetAddress
BIGINT ZipCode
i.e. I need to return one ContactInfo built from Phone where PhoneID = 128 and Address where AddressID = 268, and another where PhoneID = 129 and AddressID = 268, etc.
My big question is: How do I walk two xml arrays in lock-step in sql?
This is in SQL Server 2008 R2.
Thanks everyone :)
The only viable approach I see is trying to do this:
parse/shred your XML arrays into temporary tables for phones and addresses
those temporary tables have an ID INT IDENTITY defined
when inserting into a fresh temporary table with this INT IDENTITY, both sets of data will get consecutive numbering (1, 2, 3, 4......)
you can then join the two temporary tables on their ID and should get your "lock-step" behavior:
So in code, this would look something like (mind you: your XML is invalid, too - the attribute values need to be in double quotes for this to work!):
DECLARE #phones XML = '<PhoneID Value="128"/><PhoneID Value="129"/><PhoneID Value="130"/>'
DECLARE #Addresses XML = '<AddressID Value="268"/><AddressID Value="157"/><AddressID Value="395"/>'
DECLARE #phoneTable TABLE (ID INT IDENTITY PRIMARY KEY, PhoneID INT)
DECLARE #AddressTable TABLE (ID INT IDENTITY PRIMARY KEY, AddressID INT)
INSERT INTO #phoneTable(PhoneID)
SELECT
PHID.value('(#Value)', 'int')
FROM
#phones.nodes('/PhoneID') AS PH(PHID)
INSERT INTO #AddressTable(AddressID)
SELECT
ADRID.value('(#Value)', 'int')
FROM
#addresses.nodes('/AddressID') AS AD(ADRID)
SELECT p.*, a.*
FROM #phoneTable p
INNER JOIN #addresstable a ON p.ID = a.ID
Does that work for you?? From here, you could then insert the data into your actual working tables and do any additional lookups or processing.
Turns out it's possible to in T-SQL, like so:
CREATE PROCEDURE [dbo].[XmlParsingProcedure]
#XmlArgs XML
AS
SELECT * FROM SomeTable
WHERE (s.XmlArgs IN (select x.XmlArgs.value('#Value','smallint') FROM #XmlArgs.nodes('//XmlArgs') as x(XmlArgs)))
However, intrinsic knowledge of the XML is required.
The above implies the XML schema:
<XmlArgs Value="some value"/>
<XmlArgs Value="some other value"/>
<XmlArgs Value="yet another value"/>
where both, 'XmlArgs' and 'Value' are case sensitive.