String_Split function not splitting all strings from DB column - sql-server

I am tasked with extracting unique values from record, from a previously used 'Notes' Nvarchar(max) field.
CREATE TABLE LWArchive (
Number int,
Notes varchar(MAX))
INSERT INTO LWArchive(Number,Notes)
VALUES(1,'OGC 503360 / 503361 M303834 M303838 M303835 M303836 M303837 M303839 M303840 M303841 303842'),
(2,'OGC = Q.6773'),
(3,'DEED REF = 0001'),
(4,'OGC 50336 / 50336 M30383 M03038 M30383 M30383 M30383 M00303 M303840 M303841 M303842')
select
a.Number,
s.value
from LWArchive a
CROSS APPLY STRING_SPLIT(a.notes, N' ') s
The notes field contains multiple barcodes, which are all in a space delimited format.
E.G. 'M000001 M000002 M000003'
I have attempted to split the 'Notes' column using the query below:
select
a.ID,
s.value
from Archive a
CROSS APPLY STRING_SPLIT(a.notes, N' ') s
Unfortuntely, while some have worked correctly, the split results are inconsistant. With a number of the barcodes remaining unaffected within the space delimited results. Example Below:
When the string isn't sourced from a column & entered manually into the function. It functions as expected. Example Below:
I am using SQL server 2019, however as this is legacy data. I've had to convert the database from compatibility 110 to 130 in order to access the String_Split function.

As suggested in the comments you are likely finding an instance where the character is not a white space, but some other character.
DECLARE #string NVARCHAR(MAX) = 'M000001 M000002 M000003'
SELECT *, #string
FROM STRING_SPLIT(#string,' ')
value (No column name)
------------------------
M000001 M000001 M000002 M000003
M000002 M000001 M000002 M000003
M000003 M000001 M000002 M000003
This splits as expected. We end up with 3 rows each with one of the delimited values. If we change one of those white spaces for a tab:
SET #string = 'M000001 M000002 M000003'
SELECT *, #string
FROM STRING_SPLIT(#string,' ')
value (No column name)
--------------------------------
M000001 M000001 M000002 M000003
M000002 M000003 M000001 M000002 M000003
We can reproduce what you're seeing.
When I've faced similar problems I've found this technique to be useful:
;WITH cte AS (
SELECT #string AS completeString, ASCII(LEFT(#string, 1)) AS asciiCode, LEFT(#string, 1) AS this, RIGHT(#string, LEN(#string)-1) AS that, 1 AS charIndex
UNION ALL
SELECT completeString, ASCII(LEFT(that,1)), LEFT(that,1), RIGHT(that,LEN(that)-1), charIndex+1
FROM cte
WHERE that <> ''
)
SELECT *
FROM cte
This produces a result set for each character/glyph in the string and it's ASCII code.
completeString asciiCode this that charIndex
---------------------------------------------------------------------------------
M000001 M000002 M000003 77 M 000001 M000002 M000003 1
M000001 M000002 M000003 48 0 00001 M000002 M000003 2
M000001 M000002 M000003 48 0 0001 M000002 M000003 3
M000001 M000002 M000003 48 0 001 M000002 M000003 4
M000001 M000002 M000003 48 0 01 M000002 M000003 5
M000001 M000002 M000003 48 0 1 M000002 M000003 6
M000001 M000002 M000003 49 1 M000002 M000003 7
M000001 M000002 M000003 32 M000002 M000003 8
M000001 M000002 M000003 77 M 000002 M000003 9
M000001 M000002 M000003 48 0 00002 M000003 10
M000001 M000002 M000003 48 0 0002 M000003 11
M000001 M000002 M000003 48 0 002 M000003 12
M000001 M000002 M000003 48 0 02 M000003 13
M000001 M000002 M000003 48 0 2 M000003 14
M000001 M000002 M000003 50 2 M000003 15
M000001 M000002 M000003 9** M000003 16**
M000001 M000002 M000003 77 M 000003 17
M000001 M000002 M000003 48 0 00003 18
M000001 M000002 M000003 48 0 0003 19
M000001 M000002 M000003 48 0 003 20
M000001 M000002 M000003 48 0 03 21
M000001 M000002 M000003 48 0 3 22
M000001 M000002 M000003 51 3 23
Now we can clearly see that character 16 is actually a tab (CHAR(9)) not a white space (CHAR(32)).

Related

export and import inventory warehouse in a table

I have a table with 4 column in sql (key ID):
ID Items Import Export
1 A1 1333 0
2 A1 0 368
3 A1 0 252
4 A1 1965 0
5 A1 0 162
6 A1 0 551
7 A1 0 69
I want calculate inventory ware in a row.But not the results as expected.
You can help me get the results as below? With column Inventory
ID Items Import Export Inventory
1 A1 1333 0 1333
2 A1 0 368 965
3 A1 0 252 713
4 A1 1965 0 2678
5 A1 0 162 2516
6 A1 0 551 1965
7 A1 0 69 1896
This is my code:
Select ID,
(A.Invent + Import-Sum(Export)) as Inventory
From MyColumn,
(
Select Top 1 (Import - Export) as Invent
From MyColumn
Where Items in ('A1')
) as A
Where Items in ('A1')
Group by
A.Invent,
Import,ID
Being 2008 you're missing the sum() over, however there is another option
Example
Declare #YourTable Table ([ID] int,[Items] varchar(50),[Import] int,[Export] int)
Insert Into #YourTable Values
(1,'A1',1333,0)
,(2,'A1',0,368)
,(3,'A1',0,252)
,(4,'A1',1965,0)
,(5,'A1',0,162)
,(6,'A1',0,551)
,(7,'A1',0,69)
Select A.*
,B.*
from #YourTable A
Cross Apply (
Select Inventory = sum(Import-Export)
From #YourTable
Where Items=A.Items and ID<=A.ID
) B
Returns
ID Items Import Export Inventory
1 A1 1333 0 1333
2 A1 0 368 965
3 A1 0 252 713
4 A1 1965 0 2678
5 A1 0 162 2516
6 A1 0 551 1965
7 A1 0 69 1896
Using a join
Also window function but this is not available in 2008
declare #T table (id int identity primary key, import int, export int);
insert into #T (import, export) values
(1333, 0)
, (0, 368)
, (0, 252)
, (1965, 0)
, (0, 162)
, (0, 551)
, (0 , 69);
select t1.id, t1.import, t1.export
, sum(t2.import - t2.export) AS inven
from #T t1
join #T t2
on t2.id <= t1.id
group by t1.id, t1.import, t1.export
order by t1.id
select *
, sum(import - export) over (order by t.id) as inven
from #T t;
id import export inven
----------- ----------- ----------- -----------
1 1333 0 1333
2 0 368 965
3 0 252 713
4 1965 0 2678
5 0 162 2516
6 0 551 1965
7 0 69 1896

How can I get the differences between the OrderStop date and the OrderStart date in the next column?

How can I get the differences between the OrderStop and the OrderStart in the next column?
For example, row 1 has OrderStop date of 1/31/2007 I want to count the days between the next start date 1/26/2007 I know they are overlaps.
ID OrderStart OrderStop
132 4/14/2006 1/31/2007
132 1/26/2007 3/14/2007
132 2/1/2007 3/2/2007
132 3/2/2007 3/14/2007
132 3/14/2007 1/8/2010
132 11/26/2008 1/20/2011
132 1/8/2010 7/14/2010
132 7/14/2010 8/15/2012
132 8/15/2012 1/17/2013
132 1/17/2013 3/22/2013
132 3/21/2013 5/2/2013
132 5/2/2013 8/2/2013
132 5/22/2013 8/2/2013
132 7/29/2013 3/6/2014
132 3/5/2014 7/16/2014
132 7/16/2014 6/19/2015
132 8/21/2014 6/19/2015
132 6/19/2015 4/1/2016
132 6/25/2015 9/9/2015
132 4/1/2016 5/3/2016
132 5/3/2016 7/27/2016
132 8/15/2016 11/2/2016
I am trying to accomplished the below. How can I create a SQL statement that can accomplish this?
132 4/14/2006 1/31/2007
132 1/26/2007 4/1/2016
132 4/1/2016 7/27/2016
132 8/15/2016 11/2/2016
Consumable sample data makes things much easier for us. So does the version of SQL server. Below is a solution that uses 2012's LEAD functionality. The second one is for pre-2012 systems; it accomplishes the same thing but requires a self join and will not be as efficient.
declare #orders table (id int, orderStart date, orderStop date);
insert #orders
values
(132,'4/14/2006 ','1/31/2007'),
(132,'1/26/2007 ','3/14/2007'),
(132,'2/1/2007 ','3/2/2007 '),
(132,'3/2/2007 ','3/14/2007'),
(132,'3/14/2007 ','1/8/2010 '),
(132,'11/26/2008','1/20/2011'),
(132,'1/8/2010 ','7/14/2010'),
(132,'7/14/2010 ','8/15/2012'),
(132,'8/15/2012 ','1/17/2013'),
(132,'1/17/2013 ','3/22/2013'),
(132,'3/21/2013 ','5/2/2013 '),
(132,'5/2/2013 ','8/2/2013 '),
(132,'5/22/2013 ','8/2/2013 '),
(132,'7/29/2013 ','3/6/2014 '),
(132,'3/5/2014 ','7/16/2014'),
(132,'7/16/2014 ','6/19/2015'),
(132,'8/21/2014 ','6/19/2015'),
(132,'6/19/2015 ','4/1/2016 '),
(132,'6/25/2015 ','9/9/2015 '),
(132,'4/1/2016 ','5/3/2016 '),
(132,'5/3/2016 ','7/27/2016'),
(132,'8/15/2016 ','11/2/2016');
select *,
nextStart = lead(orderStart,1) over (order by orderStart),
daysBetween = abs(datediff(day,lead(orderStart,1) over (order by orderStart), orderStop))
from #orders
order by orderStart;
with preSort as
(
select *, rn = row_number() over (order by orderstart)
from #orders
)
select p2.id, p2.orderStart, p2.orderStop , nextStart = p1.orderStart,
daysBetween = abs(datediff(day, p2.orderStop, p1.orderStart))
from preSort p1 join preSort p2 on p1.rn = p2.rn+1
order by p1.orderStart;
Both Return
id orderStart orderStop nextStart daysBetween
----------- ---------- ---------- ---------- -----------
132 2006-04-14 2007-01-31 2007-01-26 5
132 2007-01-26 2007-03-14 2007-02-01 41
132 2007-02-01 2007-03-02 2007-03-02 0
132 2007-03-02 2007-03-14 2007-03-14 0
132 2007-03-14 2010-01-08 2008-11-26 408
132 2008-11-26 2011-01-20 2010-01-08 377
132 2010-01-08 2010-07-14 2010-07-14 0
132 2010-07-14 2012-08-15 2012-08-15 0
132 2012-08-15 2013-01-17 2013-01-17 0
132 2013-01-17 2013-03-22 2013-03-21 1
132 2013-03-21 2013-05-02 2013-05-02 0
132 2013-05-02 2013-08-02 2013-05-22 72
132 2013-05-22 2013-08-02 2013-07-29 4
132 2013-07-29 2014-03-06 2014-03-05 1
132 2014-03-05 2014-07-16 2014-07-16 0
132 2014-07-16 2015-06-19 2014-08-21 302
132 2014-08-21 2015-06-19 2015-06-19 0
132 2015-06-19 2016-04-01 2015-06-25 281
132 2015-06-25 2015-09-09 2016-04-01 205
132 2016-04-01 2016-05-03 2016-05-03 0
132 2016-05-03 2016-07-27 2016-08-15 19
132 2016-08-15 2016-11-02 NULL NULL

Loop through table and update records

I need to modify a table based on multiple fields in that same table, and i can't seem to get it right and it is becomming a real pain.
My table looks like this:
Id person code product item delivery
41 254252 1368 prodname 561352401 23
41 254252 1368 prodname 4421252401 23
42 254252 1368 prodname 2501552403 23
42 254252 1368 prodname 5281352400 23
45 254251 1368 prodname 561352401 56
46 254251 1368 prodname 2501552403 56
49 254250 1368 prodname 561352401 69
50 254250 1368 prodname 2501552403 69
1357 253288 1368 prodname 4421252401 2
1358 253288 1368 prodname 5281352400 2
1359 253288 1368 prodname 9990070900 2
1377 253263 1220 prodname 2331252400 11
1378 253263 1220 prodname 2461252403 11
1379 253263 1220 prodname 9990070900 11
I need to update the delivery column based on: person, code and product. When the value in one of these column changes it should set it's counter to 0.
I need the output to look like this:
Id person code product item delivery
41 254252 1368 prodname 561352401 23
41 254252 1368 prodname 4421252401 24
42 254252 1368 prodname 2501552403 25
42 254252 1368 prodname 5281352400 26
45 254251 1368 prodname 561352401 56
46 254251 1368 prodname 2501552403 57
49 254250 1368 prodname 561352401 69
50 254250 1368 prodname 2501552403 70
1357 253288 1368 prodname 4421252401 2
1358 253288 1368 prodname 5281352400 3
1359 253288 1368 prodname 9990070900 4
1377 253263 1220 prodname 2331252400 11
1378 253263 1220 prodname 2461252403 12
1379 253263 1220 prodname 9990070900 13
So far i have this:
DECLARE #Id INT
DECLARE #person INT
DECLARE #code VARCHAR(15)
DECLARE #product VARCHAR(10)
DECLARE #Itemtype VARCHAR(10)
DECLARE #Item VARCHAR(50)
DECLARE #Delivery INT
DECLARE sel_cursor CURSOR FOR
SELECT Id, person, code, product, delivery FROM [dbo].[orders]
WHERE Itemtype = 'ART'
ORDER BY person, product,Itemtype, Item, delivery desc
DECLARE #Counter INT
OPEN sel_cursor
set #Counter = 0
FETCH NEXT FROM sel_cursor
INTO #Id, #person, #code, #product, #Delivery
WHILE ##FETCH_STATUS = 0
BEGIN
--WHILE (SELECT COUNT(*) FROM orders WHERE person=#person AND code=#code and product=#product) > 0
BEGIN
UPDATE Orders
SET delivery = (delivery + #Counter) WHERE person=#person AND code=#code AND product=#product
set #Counter = #Counter + 1
END
FETCH NEXT FROM sel_cursor
INTO #Id, #person, #code, #product, #Delivery
set #Counter = 0
END
CLOSE sel_cursor
DEALLOCATE sel_cursor
END
DECLARE #t TABLE (
Id INT,
person INT,
code VARCHAR(10),
product VARCHAR(20),
item VARCHAR(20),
delivery INT
)
INSERT INTO #t
VALUES
(41 , 254252, 1368, 'prodname', '561352401 ', 23),
(41 , 254252, 1368, 'prodname', '4421252401', 23),
(42 , 254252, 1368, 'prodname', '2501552403', 23),
(42 , 254252, 1368, 'prodname', '5281352400', 23),
(45 , 254251, 1368, 'prodname', '561352401 ', 56),
(46 , 254251, 1368, 'prodname', '2501552403', 56),
(49 , 254250, 1368, 'prodname', '561352401 ', 69),
(50 , 254250, 1368, 'prodname', '2501552403', 69),
(1357, 253288, 1368, 'prodname', '4421252401', 2 ),
(1358, 253288, 1368, 'prodname', '5281352400', 2 ),
(1359, 253288, 1368, 'prodname', '9990070900', 2 ),
(1377, 253263, 1220, 'prodname', '2331252400', 11),
(1378, 253263, 1220, 'prodname', '2461252403', 11),
(1379, 253263, 1220, 'prodname', '9990070900', 11)
;WITH cte AS
(
SELECT *, RowNum = ROW_NUMBER() OVER (
PARTITION BY Person, Code, product
ORDER BY 1/0) - 1
FROM #t
)
UPDATE cte
SET delivery += RowNum
SELECT * FROM #t
Output -
Id person code product item delivery
----------- ----------- ---------- ---------- ------------- -----------
41 254252 1368 prodname 561352401 23
41 254252 1368 prodname 4421252401 24
42 254252 1368 prodname 2501552403 25
42 254252 1368 prodname 5281352400 26
45 254251 1368 prodname 561352401 56
46 254251 1368 prodname 2501552403 57
49 254250 1368 prodname 561352401 69
50 254250 1368 prodname 2501552403 70
1357 253288 1368 prodname 4421252401 2
1358 253288 1368 prodname 5281352400 3
1359 253288 1368 prodname 9990070900 4
1377 253263 1220 prodname 2331252400 11
1378 253263 1220 prodname 2461252403 12
1379 253263 1220 prodname 9990070900 13

Compare rows value

ID mobileno dateofregistration registrationstate
44 1674174925 2011-04-18 10:17:30.670 0
45 1677864168 2011-04-18 10:20:22.450 1
46 1677864161 2011-04-18 20:47:35.293 0
47 1674174925 2011-04-19 09:28:55.200 1
48 1674174925 2011-04-19 09:28:56.200 0
49 1674174925 2011-04-19 09:28:57.200 1
50 1674174925 2011-04-18 10:17:30.670 0
51 1677864168 2011-04-18 10:20:22.450 1
52 1677864161 2011-04-20 20:47:35.293 0
53 1674174925 2011-04-22 09:28:55.200 1
54 1674174925 2011-04-28 09:28:56.200 0
55 1674174925 2011-04-28 09:28:57.200 1
My problem is how to count the rows with the following conditions:
registrationstate = 1 and datediff(day,dateofregistration) > 30 for the individual mobile no.
Here the result will be 1
first of all, datediff requires 3 arguments
DATEDIFF ( datepart , startdate , enddate )
I assume that you want to compare with today's date, so you can use getdate()
select * from yourtable
where registrationstate = 1
and datediff( day, dateofregistration, getdate() ) > 30
if getdate() return 2011-05-04 , you will get the following output
------------------------------------------------------------
ID mobileno dateofregistration registrationstate
------------------------------------------------------------
45 1677864168 2011-04-18 10:20:22.450 1
47 1674174925 2011-04-19 09:28:55.200 1
49 1674174925 2011-04-19 09:28:57.200 1
51 1677864168 2011-04-18 10:20:22.450 1
53 1674174925 2011-04-22 09:28:55.200 1
55 1674174925 2011-04-28 09:28:57.200 1
------------------------------------------------------------
now you want to group the results by mobileno and also return the number of rows per group. You can use the COUNT(*) aggregate function to return the number of rows per group
select COUNT(*) as total, mobileno from yourtable
where registrationstate = 1
and datediff( day, dateofregistration, getdate() ) > 30
group by mobileno
you will get the following output
------------------------------------------------------------
mobileno total
------------------------------------------------------------
1677864168 2
1674174925 4
------------------------------------------------------------

How do I get a total from a SQL query with a caculated field

I have a table that lists visits to a clinic. I'd like to get a "histogram" of sorts showing how frequently patients visit the clinic along with totals. Here's some sample code (tested under MS SQL Server 2005) to show what I'm talking about:
CREATE TABLE #test (
visit_id int IDENTITY(1,1),
patient_id int
);
DECLARE #num_patients int;
SELECT #num_patients = 1000 + ABS(CHECKSUM(NEWID())) % 250;
INSERT INTO #test (patient_id)
SELECT TOP 15 PERCENT ABS(CHECKSUM(NEWID())) % #num_patients
FROM sysobjects a, sysobjects b;
-- SELECT COUNT(*) AS total_visits FROM #test;
-- SELECT COUNT(DISTINCT patient_id) AS distinct_patients FROM #test;
SELECT CASE GROUPING(num_pat_visits) WHEN 1 THEN 'Total'
ELSE CAST(num_pat_visits AS varchar(5)) END AS num_pat_visits,
COUNT(*) AS num_patients, num_pat_visits * COUNT(*) AS tot_pat_visit
FROM
(SELECT patient_id, COUNT(*) AS num_pat_visits FROM #test GROUP BY patient_id) a
GROUP BY num_pat_visits WITH ROLLUP
ORDER BY CAST(num_pat_visits AS int) DESC;
This gets me almost to where I want:
num_pat_visits num_patients tot_pat_visit
-------------- ------------ -------------
60 1 60
54 2 108
52 2 104
51 4 204
50 3 150
49 3 147
48 7 336
47 7 329
46 15 690
45 15 675
44 29 1276
43 36 1548
42 45 1890
41 45 1845
40 59 2360
39 71 2769
38 51 1938
37 72 2664
36 77 2772
35 74 2590
34 72 2448
33 82 2706
32 90 2880
31 74 2294
30 69 2070
29 47 1363
28 30 840
27 27 729
26 26 676
25 21 525
24 13 312
23 4 92
22 5 110
21 4 84
20 2 40
18 2 36
Total 1186 NULL
However, I can't seem to get SQL Server to display the total number of visits where it says NULL on the total row.
Any ideas?
I think you can just do:
sum(num_pat_visits) as tot_pat_visit
SELECT CASE GROUPING(num_pat_visits) WHEN 1 THEN 'Total'
ELSE CAST(num_pat_visits AS varchar(5)) END AS num_pat_visits,
COUNT(*) AS num_patients,
--num_pat_visits * COUNT(*) AS tot_pat_visit
sum(num_pat_visits) as tot_pat_visit
FROM
(SELECT patient_id, COUNT(*) AS num_pat_visits FROM #test GROUP BY patient_id) a
GROUP BY num_pat_visits WITH ROLLUP
ORDER BY CAST(num_pat_visits AS int) DESC;

Resources