How to concatenate the "overflow" of fields with character limits - sql

I have a table with 3 address fields and each address field has a limit of 100 characters each.
I need to create a query to make the maximum character limit for each address field to be 30 characters long. If one address field is > 30 then I'll cut off the rest, but take the remainder and concatenate it onto the beginning of the next address field. I would do this until the last address field (address3) is filled up and then just get rid of the remainder on the last address field.
Is there a way to do this with an SQL query or with T-SQL?

You don't specify what to do with very short addresses, but my first crack at it would be something like this:
with temp as
(
select 1 id, 'abcdefghijklmnopqrstuvwxyz123456789' part1, 'second part' part2, 'third part' part3
),
concated as
(
SELECT id, part1 + part2 + part3 as whole
FROM temp
)
select id,
SUBSTRING(whole, 0, 30) f,
SUBSTRING(whole, 30,30) s,
SUBSTRING(whole, 60,30) t
from concated
This returns:
id | f | s | t
1 | abcdefghijklmnopqrstuvwxyz123 | 456789second partthird part |
If that's not what you're looking for please specify the desired output for the above.
UPDATE:
Well... this appears to work but it's pretty gross. I'm sure someone can come up with a better solution.
with temp as
(
select 1 id, 'abcdefghijklmnopqrstuvwxyz123456789 ' part1, 'second part' part2, 'third part' part3
)
select id,
SUBSTRING(part1, 0, 30) f,
SUBSTRING(SUBSTRING(part1, 30, 70) + SUBSTRING(part2, 0,30),0,30) s,
SUBSTRING(SUBSTRING(SUBSTRING(SUBSTRING(part1, 30, 70) + SUBSTRING(part2, 0,30),30,70),0,30) + SUBSTRING(part3, 0,30),0,30) t
from temp

I think I'd go with the problem description, and write something that's "obviously" correct (provided I've understood your spec :-))
/* Setup data - second example stolen from Abe, first just showing that it works with short enough data */
declare #t table (ID int not null,Address1 varchar(100) not null,Address2 varchar(100) not null,Address3 varchar(100) not null)
insert into #t (ID,Address1,Address2,Address3)
values (1,'abc','def','ghi'),
(2,'abcdefghijklmnopqrstuvwxyz123456789 ', 'second part', 'third part')
/* Actual query - shift address pieces through the address fields, but only to later ones */
;with Shift1 as (
select
ID,SUBSTRING(Address1,1,30) as Address1,SUBSTRING(Address1,31,70) as Address1Over,Address2,Address3
from #t
), Shift2 as (
select
ID,Address1,SUBSTRING(Address1Over+Address2,1,30) as Address2,SUBSTRING(Address1Over+Address2,31,70) as Address2Over,Address3
from Shift1
), Shift3 as (
select
ID,Address1,Address2,SUBSTRING(Address2Over+Address3,1,30) as Address3
from Shift2
)
select * from Shift3
Result:
ID Address1 Address2 Address3
----------- ------------------------------ ------------------------------ ------------------------------
1 abc def ghi
2 abcdefghijklmnopqrstuvwxyz1234 56789 second part third part

Related

How do I select records from a table and order it by ID in a comma separated string?

I am using Microsoft SQL Server.
Let's say I have a table, with the following fields:
ID, NAME
=========
1, John
2, Peter
3, Jenny
4, Robert
5, Alan
And, I have a comma separated string with the ID from the table above. Let's say the string have the following value:
Sort_order = "3,5,4,1,2"
How do I select records from the table and order it by Sort_order?
The result set from the select should give records in the following order:
3, Jenny
5, Alan
4, Robert
1, John
2, Peter
You can try this script-
DEMO HERE
--Consider this first parameter is you retrieved
--order string from another table
DECLARE #order_ VARCHAR(MAX) = '3,5,4,1,2'
--Added Comma at the start and end of the order string
--So that id 1 and 11 do not conflict
DECLARE #order_new VARCHAR(MAX) = ','+#order_+','
SELECT *
FROM your_table
ORDER BY CHARINDEX(
','+CAST(id AS VARCHAR)+',
',#order_new,
0
)
You can also ignore the second step of adding additional Comma to the order string and do it directly in the script as below which will return the same output-
SELECT *
FROM your_table
ORDER BY CHARINDEX(
','+CAST(id AS VARCHAR)+',',
','+#order_+',',
0
)
Here is the final output-

SQL: Extract last 5 digits in a string after special char

I am struggling to extract last 5 digits in title(free text field) after special char ': ' (with a space). Sample records are as follows:
title column
1 ABC Requirement1 - 1,500 - 3,000 sq m : 12345
2 10,000 sft shed requirement
3 OFFICES REQUIRED 500/700 SQ FT : 56789
4 Land Acquisition : 34567
5 Storage Requirement : 12345
6 Land Requirement :100 sq.m
my result set should be as follows:
ID
1 12345
3 56789
4 34567
5 12345
It should only pick up last 5 digits(ID) after special char ': ' and ignore other records with ': ' in between. I am trying to extract ID values to join with another table. Any help is highly appreciated!
This should get the query that you want.
SELECT LEFT(SUBSTRING(Title, CHARINDEX(': ', Title) + 2, LEN(Title)), 5)
FROM #table
WHERE [Title] LIKE '%: %'
AND ISNUMERIC(LEFT(SUBSTRING(Title, CHARINDEX(': ', Title) + 2, LEN(Title)), 5)) = 1
Try this query --
;WITH CTE
AS (
SELECT Id
,CASE
WHEN CHARINDEX(':', Title, 1) > 1
THEN SUBSTRING(Title, CHARINDEX(':', Title, 1) + 2, 5)
END AS TitleID
FROM RequirementTable
)
SELECT ID
,TitleID
FROM CTE
WHERE ISNUMERIC(TitleID) = 1;
First, you should seriously reconsider the way you're storing your data if you need to go to these lengths to form a relation between records. This is potentially disastrous should your data ever include ': ' naturally and without ending in a foreign key value. And you most likely won't figure that out until it's too late and processing and/or other applications fail as a result.
However, to answer the question as it was asked, I have the same thing as #ChesterLin, but with sample data and including the 'ID' column in the output.
DECLARE #Temp TABLE (ID int, Title varchar(255))
INSERT INTO #Temp
VALUES
(1, 'ABC Requirement1 - 1,500 - 3,000 sq m : 12345'),
(2, '10,000 sft shed requirement'),
(3, 'OFFICES REQUIRED 500/700 SQ FT : 56789'),
(4, 'Land Acquisition : 34567'),
(5, 'Storage Requirement : 12345'),
(6, 'Land Requirement :100 sq.m')
SELECT ID, LEFT(SUBSTRING(Title, CHARINDEX(': ', Title) + 2, LEN(Title)), 5) AS [Extracted Value]
FROM #Temp
WHERE [Title] LIKE '%: %'
AND ISNUMERIC(LEFT(SUBSTRING(Title, CHARINDEX(': ', Title) + 2, LEN(Title)), 5)) = 1
you can get last 5 digits
SUBSTR(column, LENGTH(column) - 5, 5)
OR
SELECT RIGHT('ABC Requirement1 - 1,500 - 3,000 sq m : 12345',5)
OR Full query
SELECT substr(title, character(title)-5) from table_name;
substr(column, -5, 5)
Starts from the last character in the string, and gives the five characters.
Then cast it as INT.
select cast(substr(column, -5, 5) as INT) as ID from table_name
where isnumeric(substr(column, -5, 5)) = 1
I hope this will work. Or, something like this.

Combining duplicate records in SQL Server

I have a table in SQL Server 2012 that holds a list of parts, location of the parts and the quantity on hand. The problem I have is someone put a space in front of the location when they added it to the database. This allowed there to be two records.
I need to create a job that will find the parts with spaces before the location and add those parts to the identical parts without spaces in front of the location. I'm not quite sure where to even start with this.
This is the before:
Partno | PartRev | Location | OnHand | Identity_Column
--------------------------------------------------------------------
0D6591D 000 MV3 55.000 103939
0D6591D 000 MV3 -55.000 104618
This is what I would like to have after the job ran:
Partno | PartRev | Location | OnHand | Identity_Column
--------------------------------------------------------------------
0D6591D 000 MV3 0 104618
Two steps: 1. update the records with the correct locations, 2. delete the records with the wrong locations.
update mytable
set onhand = onhand +
(
select coalesce(sum(wrong.onhand), 0)
from mytable wrong
where wrong.location like ' %'
and trim(wrong.location) = mytable.location
)
where location not like ' %';
delete from mytable where location like ' %';
You can do some grouping with a HAVING clause on to identify the records. I've used REPLACE to replace spaces with empty strings in the location column, you could also use LTRIM and RTRIM:
CREATE TABLE #Sample
(
[Partno] VARCHAR(7) ,
[PartRev] INT ,
[Location] VARCHAR(5) ,
[OnHand] INT ,
[Identity_Column] INT
);
INSERT INTO #Sample
([Partno], [PartRev], [Location], [OnHand], [Identity_Column])
VALUES
('0D6591D', 000, ' MV3', 55.000, 103939),
('0D6591D', 000, 'MV3', -55.000, 104618)
;
SELECT Partno ,
PartRev ,
REPLACE( Location, ' ', '') Location,
SUM(OnHand) [OnHand]
FROM #Sample
GROUP BY REPLACE(Location, ' ', '') ,
Partno ,
PartRev
HAVING COUNT(Identity_Column) > 1;
DROP TABLE #Sample;
Produces:
Partno PartRev Location OnHand
0D6591D 0 MV3 0

Table Normalization (Parse comma separated fields into individual records)

I have a table like this:
Device
DeviceId Parts
1 Part1, Part2, Part3
2 Part2, Part3, Part4
3 Part1
I would like to create a table 'Parts', export data from Parts column to the new table. I will drop the Parts column after that
Expected result
Parts
PartId PartName
1 Part1
2 Part2
3 Part3
4 Part4
DevicePart
DeviceId PartId
1 1
1 2
1 3
2 2
2 3
2 4
3 1
Can I do this in SQL Server 2008 without using cursors?
-- Setup:
declare #Device table(DeviceId int primary key, Parts varchar(1000))
declare #Part table(PartId int identity(1,1) primary key, PartName varchar(100))
declare #DevicePart table(DeviceId int, PartId int)
insert #Device
values
(1, 'Part1, Part2, Part3'),
(2, 'Part2, Part3, Part4'),
(3, 'Part1')
--Script:
declare #DevicePartTemp table(DeviceId int, PartName varchar(100))
insert #DevicePartTemp
select DeviceId, ltrim(x.value('.', 'varchar(100)'))
from
(
select DeviceId, cast('<x>' + replace(Parts, ',', '</x><x>') + '</x>' as xml) XmlColumn
from #Device
)tt
cross apply
XmlColumn.nodes('x') as Nodes(x)
insert #Part
select distinct PartName
from #DevicePartTemp
insert #DevicePart
select tmp.DeviceId, prt.PartId
from #DevicePartTemp tmp
join #Part prt on
prt.PartName = tmp.PartName
-- Result:
select *
from #Part
PartId PartName
----------- ---------
1 Part1
2 Part2
3 Part3
4 Part4
select *
from #DevicePart
DeviceId PartId
----------- -----------
1 1
1 2
1 3
2 2
2 3
2 4
3 1
You will need a Tally table to accomplish this without a cursor.
Follow the instructions to create a tally table here: Tally Tables by Jeff Moden
This script will put the table into your Temp database, so you probably want to change the "Use DB" statement
Then you can run the script below to insert a breakdown of Devices and Parts into a temp table. You should then be able to join on your part table by the part name (to get the ID) and insert into your new DevicePart table.
select *,
--substring(d.parts, 1, t.n)
substring(d.parts, t.n, charindex(', ', d.parts + ', ',t.n) - t.n) 'Part'
into #devicesparts
from device d
cross join tally t
where t.n < (select max(len(parts))+ 1 from device)
and substring(', ' + d.parts, t.n, 1) = ', '
Have a look at using fn_Split to create a table variable from the comma separated values.
You can then use this to drive your insert.
EDIT: Actually, I think you may still need a cursor. Leaving this answer incase fn_Split helps.
If there is a maximum number of parts per device then, yes, it can be done without a cursor, but this is quite complex.
Essentially, create a table (or view or subquery) that has a DeviceID and one PartID column for each possible index in the PartID string. This can be accomplished by making the PartID columns calculated columns using fn_split or another method of your choice. From there you do a multiple self-UNION of this table, with one table in the self-UNION for each PartID column. Each table in the self-UNION has only one of the PartID columns included in the select list of the query for the table.

Help with string formatting in SQL Server Query

I have the following SQL query:
SELECT DISTINCT ProductNumber, PageNumber FROM table
I am trying to modify the query so that PageNumber will be formatted. You see, PageNumber is in any of the following formats, where 'x' is a digit:
xxx, xxx
xxx
xxx-xxx
xx, xxx-xxx
xx-xx, xxx
xx-xx, xxx-xxx
I want to format PageNumber so that it is only in the format: xxx. To do so, I have parse out the following bolded numbers from the above formats:
xxx, xxx
xxx
xxx-xxx
xx, xxx-xxx
xx-xx, xxx
xx-xx, xxx-xxx
I want to do this all without writing any functions, but I don't know if that is possible. I am having trouble "detecting" all of the different formats, though:
Here is what I have so far:
SELECT ProductNumber,
CASE WHEN CHARINDEX(',', PageNumber) > 0
THEN SUBSTRING(PageNumber, 0, CHARINDEX('-', PageNumber))
WHEN CHARINDEX('-', PageNumber) > 0
THEN SUBSTRING(PageNumber, 0, CHARINDEX('-', PageNumber))
ELSE PageNumber
END AS PageNumber
FROM table
WHERE PageNumber IS NOT NULL
AND PageNumber <> ''
Can anyone offer me some help? Thanks!
Use pattern matching rather than CHARINDEX
CASE also forces ordering of evaluation which helps here for the 3rd case which overlaps with the first 2 cases.
Not tested, something like
CASE
WHEN PageNumber LIKE '[0-9][0-9][0-9]%' THEN LEFT(PageNumber, 3)
WHEN PageNumber LIKE '[0-9][0-9]-[0-9][0-9], [0-9][0-9][0-9]') THEN RIGHT(PageNumber , 3)
WHEN PageNumber LIKE '[0-9][0-9]%') THEN LEFT(PageNumber, 2)
END
try this:
DECLARE #YourTable table (ProductNumber int, PageNumber varchar(20))
INSERT #YourTable VALUES (1,'123, 456')
INSERT #YourTable VALUES (2,'123')
INSERT #YourTable VALUES (3,'123-456')
INSERT #YourTable VALUES (4,'12, 345-678')
INSERT #YourTable VALUES (5,'12-34, 567')
INSERT #YourTable VALUES (6,'12-34, 567-789')
;WITH AllNumbers AS ---builds a Numbers table 1-100
( SELECT 1 AS Number
UNION ALL
SELECT Number+1
FROM AllNumbers
WHERE Number<101
)
, RowChars AS --one row for each non-numeric single character value per #YourTable row
( SELECT DISTINCT
ProductNumber,Number, SUBSTRING(PageNumber,Number,1) AS CharacterOF
FROM #YourTable
INNER JOIN AllNumbers ON 1=1
WHERE SUBSTRING(PageNumber,Number,1) IS NOT NULL AND SUBSTRING(PageNumber,Number,1) NOT LIKE '[0-9]' AND SUBSTRING(PageNumber,Number,1)!=''
)
,FirstSplit AS --get first non-numeric single character value per #YourTable row
( SELECT
ProductNumber,MIN(Number) AS SplitOf
FROM RowChars
GROUP BY ProductNumber
)
SELECT
t.ProductNumber, LEFT(t.PageNumber,COALESCE(s.SplitOf-1,LEN(t.PageNumber))) AS NewPage,t.PageNumber AS OldPage
FROM #YourTable t
LEFT OUTER JOIN FirstSplit s ON t.ProductNumber=s.ProductNumber
OUTPUT:
ProductNumber NewPage OldPage
------------- -------------------- --------------------
1 123 123, 456
2 123 123
3 123 123-456
4 12 12, 345-678
5 12 12-34, 567
6 12 12-34, 567-789
(6 row(s) affected)