I have SQL code like this:
SELECT
CASE WHEN TRY_CONVERT(int, strCol) IS NULL
THEN strCol
ELSE CONVERT(VARCHAR, CONVERT(int, strCol))
END
Table is as follow:
| strCol |
|--------|
| 000373 |
| 2AB38 |
| C2039 |
| ABC21 |
| 32BC |
I wish to drop all the leading 0s in rows with pure number
| strCol |
|--------|
| 373 |
| 2AB38 |
| C2039 |
| ABC21 |
| 32BC |
But I got the following error:
Conversion failed when converting the varchar value '2AB38' to data type int.
I don't quite understand, it should not even enter the second case branch isn't it?
Yet another option is try_convert in concert with a coalesce
Example
Declare #YourTable Table ([strCol] varchar(50)) Insert Into #YourTable Values
('000373')
,('2AB38')
,('C2039')
,('ABC21')
,('32BC')
Select *
,NewVal = coalesce(left(try_convert(int,strCol),10),strCol)
From #YourTable
Returns
strCol NewVal
000373 373
2AB38 2AB38
C2039 C2039
ABC21 ABC21
32BC 32BC
Thank you so much #Dale K and #Programnik
CASE WHEN ISNUMERIC(strCol) = 0
THEN strCol
ELSE TRY_CONVERT(VARCHAR, TRY_CONVERT(int, strCol))
END AS strCol
These is the piece of code got the work done.
All branches are evaluated, there are no guarantee it will short circuit #Dale K
Can use ISNUMERIC in SQL SERVER #Programnik
Related
I have table 1 data and I need to create another column as on criteria
If id has "-" or "any letters" bring value from invoice
Table1
+---------+---------+
| id | invoice |
+---------+---------+
| 1234 | 2534 |
| 9870 | 6542 |
| ABC234 | 9874 |
| 34-5469 | 325416 |
+---------+---------+
Expected Result as id2
+---------+---------+--------+
| id | invoice | id2 |
+---------+---------+--------+
| 1234 | 2534 | 1234 |
| 9870 | 6542 | 9870 |
| ABC234 | 9874 | 9874 |
| 34-5469 | 325416 | 325416 |
+---------+---------+--------+
You can use isnumeric function to find out whether the id is int or not.
select *,
case when isnumeric(id) = 1 then id else invoice end as id2
from [yourtable]
Edit: isnumeric is not providing credible results all the time and hence if you are using SQL server 2012 or 2014 and above you may go for try_cast
select *
,case when try_cast(id as int) is not null then id else invoice end as id2
from [yourtable]
Assuming that you are just looking for values that have a letter or a hyphen (-) you could use a CASE expression and a LIKE like this:
SELECT CASE WHEN id LIKE '%[A-z-]%' THEN invoice ELSE id END
FROM dbo.YourTable;
What would likely be better, however, is to check that id doesn't value any characters apart from digits:
SELECT CASE WHEN id LIKE '%[^0-9]%' THEN invoice ELSE id END
FROM dbo.YourTable;
I know that this is possible in Oracle and I wonder if SQL Server also supports it (searched for answer without success).
It would greatly simplify my life in the current project if I could define a column of a table to be a table itself, something like:
Table A:
Column_1 Column_2
+----------+----------------------------------------+
| 1 | Columns_2_1 Column_2_2 |
| | +-------------+------------------+ |
| | | 'A' | 12345 | |
| | +-------------+------------------+ |
| | | 'B' | 777777 | |
| | +-------------+------------------+ |
| | | 'C' | 888888 | |
| | +-------------+------------------+ |
+----------+----------------------------------------+
| 2 | Columns_2_1 Column_2_2 |
| | +-------------+------------------+ |
| | | 'X' | 555555 | |
| | +-------------+------------------+ |
| | | 'Y' | 666666 | |
| | +-------------+------------------+ |
| | | 'Z' | 000001 | |
| | +-------------+------------------+ |
+----------+----------------------------------------+
Thanks in advance.
There is one option where you can store data as XML
Declare #YourTable table (ID int,XMLData xml)
Insert Into #YourTable values
(1,'<root><ID>1</ID><Active>1</Active><First_Name>John</First_Name><Last_Name>Smith</Last_Name><EMail>john.smith#email.com</EMail></root>')
,(2,'<root><ID>2</ID><Active>0</Active><First_Name>Jane</First_Name><Last_Name>Doe</Last_Name><EMail>jane.doe#email.com</EMail></root>')
Select ID
,Last_Name = XMLData.value('(root/Last_Name)[1]' ,'nvarchar(50)')
,First_Name = XMLData.value('(root/First_Name)[1]' ,'nvarchar(50)')
From #YourTable
Returns
ID Last_Name First_Name
1 Smith John
2 Doe Jane
Actually, for a normalized database we do not require such functionality.
Because if we need to insert a table within a column than we can create a child table and reference it as a foreign key in the parent table.
In spite, if you still insist to such functionality than you can use SQL Server 2016 to support JSON data where you can store any associative list in JSON format.
Like:
DECLARE #json NVARCHAR(4000)
SET #json =
N'{
"info":{
"type":1,
"address":{
"town":"Bristol",
"county":"Avon",
"country":"England"
},
"tags":["Sport", "Water polo"]
},
"type":"Basic"
}'
SELECT
JSON_VALUE(#json, '$.type') as type,
JSON_VALUE(#json, '$.info.address.town') as town,
JSON_QUERY(#json, '$.info.tags') as tags
SELECT value
FROM OPENJSON(#json, '$.info.tags')
In older versions, this can be achieved through xml as shown in previous answer.
Your can also make use of "sql_variant" datatype to map your table.
Previously, I was also in search of such features as available in Oracle. But after reading various articles and blogs from experts, I was convinced, such features will make the things more complex beside helping.
Only storing the data in required format is not important, It is worthy when it is also efficiently available (readable).
Hope this will help you to take your decision.
I want to query in SQL Server a column's name. I know it is possible to get a table's columns from the system table, but unfortunately that's not enough for me.
Example:
I have a table that contains an ID column and a string column. The table's name is test, and it has a testID and a test column.
This query:
select column_name
from information_schema.columns
where table_name = 'teszt'
return the names of the columns of my table. So it returns testID and Test.
What I want is when I use a query like this:
select count(*) as Amount from test
I want a query that can return the column names of my query. So in this specific case it returns the string 'Amount'. I don't know if that is possible.
Not sure if there is an easier way of getting the name of columns with aliases, but one way of doing it is via XML. This query will return one row per column in the inner query:
select T1.res.value('local-name(.)', 'varchar(50)')
from (select cast(
(
select count(*) as Amount from test
for xml raw) as xml
)) q(res)
CROSS APPLY q.res.nodes('/row/#*') as T1(res)
In SQL Server 2012 you have a stored procedure that you can use for exactly this purpose.
sp_describe_first_result_set (Transact-SQL)
SQL Fiddle
MS SQL Server 2012 Schema Setup:
create table test(id int);
Query 1:
exec sp_describe_first_result_set N'select count(*) as Amount from test'
Results:
| IS_HIDDEN | COLUMN_ORDINAL | NAME | IS_NULLABLE | SYSTEM_TYPE_ID | SYSTEM_TYPE_NAME | MAX_LENGTH | PRECISION | SCALE | COLLATION_NAME | USER_TYPE_ID | USER_TYPE_DATABASE | USER_TYPE_SCHEMA | USER_TYPE_NAME | ASSEMBLY_QUALIFIED_TYPE_NAME | XML_COLLECTION_ID | XML_COLLECTION_DATABASE | XML_COLLECTION_SCHEMA | XML_COLLECTION_NAME | IS_XML_DOCUMENT | IS_CASE_SENSITIVE | IS_FIXED_LENGTH_CLR_TYPE | SOURCE_SERVER | SOURCE_DATABASE | SOURCE_SCHEMA | SOURCE_TABLE | SOURCE_COLUMN | IS_IDENTITY_COLUMN | IS_PART_OF_UNIQUE_KEY | IS_UPDATEABLE | IS_COMPUTED_COLUMN | IS_SPARSE_COLUMN_SET | ORDINAL_IN_ORDER_BY_LIST | ORDER_BY_IS_DESCENDING | ORDER_BY_LIST_LENGTH | TDS_TYPE_ID | TDS_LENGTH | TDS_COLLATION_ID | TDS_COLLATION_SORT_ID |
|-----------|----------------|--------|-------------|----------------|------------------|------------|-----------|-------|----------------|--------------|--------------------|------------------|----------------|------------------------------|-------------------|-------------------------|-----------------------|---------------------|-----------------|-------------------|--------------------------|---------------|-----------------|---------------|--------------|---------------|--------------------|-----------------------|---------------|--------------------|----------------------|--------------------------|------------------------|----------------------|-------------|------------|------------------|-----------------------|
| 0 | 1 | Amount | 1 | 56 | int | 4 | 10 | 0 | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | 0 | 0 | 0 | (null) | (null) | (null) | (null) | (null) | 0 | (null) | 0 | 0 | 0 | (null) | (null) | (null) | 38 | 4 | (null) | (null) |
Maybe you want something like this? :-)
SELECT AMOUNT
FROM
(
SELECT COUNT(*) AS AMOUNT
FROM TEST
)X
I've been given the task at work of creating a report based on a very poorly designed table structure.
Consider the following two tables. They contain techniques that each person likes to perform at each gym. Keep in mind that a unique person may show up on multiple rows in the PERSONNEL table:
PERSONNEL
+-----+-----+-------+--------+-----------+
| ID | PID | Name | Gym | Technique |
+-----+-----+-------+--------+-----------+
| 1 | 122 | Bob | GymA | 2,3,4 |
+-----+-----+-------+--------+-----------+
| 2 | 131 | Mary | GymA | 1,2,4 |
+-----+-----+-------+--------+-----------+
| 3 | 122 | Bob | GymB | 1,2,3 |
+-----+-----+-------+--------+-----------+
TECHNIQUES
+-----+------------+
| ID | Technique |
+-----+------------+
| 1 | Running |
+-----+------------+
| 2 | Walking |
+-----+------------+
| 3 | Hopping |
+-----+------------+
| 4 | Skipping |
+-----+------------+
What I am having trouble coming up with is a MSSQL query that will reliably give me a listing of every person in the table that is performing a certain technique.
For instance, let's say that I want a listing of every person that likes skipping. The desired results would be:
PREFERS_SKIPPING
+-----+-------+--------+
| PID | Name | Gym |
+-----+-------+--------+
| 122 | Bob | GymA |
+-----+-------+--------+
| 131 | Mary | GymA |
+-----+-------+--------+
Likewise hopping:
PREFERS_HOPPING
+-----+-------+--------+
| PID | Name | Gym |
+-----+-------+--------+
| 122 | Bob | GymA |
+-----+-------+--------+
| 122 | Bob | GymB |
+-----+-------+--------+
I can break out the strings easily in ColdFusion, but that isn't an option due to the size of the PERSONNEL table. Can anyone help?
I think this query looks cleaner:
SELECT p.*,
t.Technique as ParsedTechnique
FROM Personnel p
JOIN Techniques t
ON CHARINDEX((','+CAST(t.id as varchar(10))+','), (','+p.technique+',')) > 0
WHERE t.id ='1';
You can just change the WHERE t.id = to whatever TechniqueId you need.
Fiddle Here
Using this function
Create FUNCTION F_SplitAsIntTable
(
#txt varchar(max)
)
RETURNS
#tab TABLE
(
ID int
)
AS
BEGIN
declare #i int
declare #s varchar(20)
Set #i = CHARINDEX(',',#txt)
While #i>1
begin
set #s = LEFT(#txt,#i-1)
insert into #tab (id) values (#s)
Set #txt=RIGHT(#txt,Len(#txt)-#i)
Set #i = CHARINDEX(',',#txt)
end
insert into #tab (id) values (#txt)
RETURN
END
You can query like this
declare #a Table (id int,Name varchar(10),Kind Varchar(100))
insert into #a values (1,'test','1,2,3,4'),(2,'test2','1,2,3,5'),(3,'test3','3,5')
Select a.ID,Name
from #a a
cross apply F_SplitAsIntTable(a.Kind) b
where b.ID=2
One of the problems you have to prevent is prevent "1" from matching "10" and "11". For this, you want to be sure that all values are delimited by the separator (in this case a comma).
Here is a method using like that should work effectively (although performance will not be so great):
SELECT p.*, t.Technique as ParsedTechnique
FROM Personnel p join
Techniques t
on ','+p.technique+',' like '%,'+cast(t.id as varchar(255))+',%'
WHERE t.id = 1;
If performance is an issue, then fix your data structure an include a PersonTechniques table so you can do a proper join.
The first comment under the question provided the link to the answer. Here's what I ended up going with:
WHERE
p.Technique LIKE '%,29,%' --middle
OR
p.Technique LIKE '29,%' --start
OR
p.Technique LIKE '%,29' --end
OR
p.Technique = '29' --single (good point by Cheran S in comment)
At initial glance I thought it wouldn't work, but clever use of % made it not match ids like 129, etc.
Sorry I know that's a rubbish Title but I couldn't think of a more concise way of describing the issue.
I have a (MSSQL 2008) table that contains telephone numbers:
| CustomerID | Tel1 | Tel2 | Tel3 | Tel4 | Tel5 | Tel6 |
| Cust001 | 01222222 | 012333333 | 07111111 | 07222222 | 01222222 | NULL |
| Cust002 | 07444444 | 015333333 | 07555555 | 07555555 | NULL | NULL |
| Cust003 | 01333333 | 017777777 | 07888888 | 07011111 | 016666666 | 013333 |
I'd like to:
Remove any duplicate phone numbers
Rearrange the telephone numbers so that anything beginning with "07" is the first phone number. If there are multiple 07's, they should be in the first fields. The order of the numbers apart from that doesn't really matter.
So, for example, after processing, the table would look like:
| CustomerID | Tel1 | Tel2 | Tel3 | Tel4 | Tel5 | Tel6 |
| Cust001 | 07111111 | 07222222 | 01222222 | 012333333 | NULL | NULL |
| Cust002 | 07444444 | 07555555 | 015333333 | NULL | NULL | NULL |
| Cust003 | 07888888 | 07011111 | 016666666 | 013333 | 01333333 | 017777777 |
I'm struggling to figure out how to efficiently achieve my goal (there are 600,000+ records in the table). Can anyone help?
I've created a fiddle if it'll help anyone play around with the scenario.
You can break up the numbers into individual rows using UNPIVOT, then reorder them based on the occurence of the '07' prefix using ROW_NUMBER(), and finally recombine it using PIVOT to end up with the 6 Tel columns again.
select *
FROM
(
select CustomerID, Col, Tel
FROM
(
select *, Col='Tel' + RIGHT(
row_number() over (partition by CustomerID
order by case
when Tel like '07%' then 1
else 2
end),10)
from phonenumbers
UNPIVOT (Tel for Seq in (Tel1,Tel2,Tel3,Tel4,Tel5,Tel6)) seqs
) U
) P
PIVOT (MAX(TEL) for Col IN (Tel1,Tel2,Tel3,Tel4,Tel5,Tel6)) V;
SQL Fiddle
Perhaps using cursor to collect all customer id and sorting the fields...traditional sorting technique as we used to do in school c++ ..lolz...like to know if any other method possible.
If you dont get any then it is the last way . It will take a long time for sure to execute.