Dynamic T-SQL query for hierarchical data - sql-server

I am trying to do a dynamic query to go through child - parents table and I have been able to go through top and second level of hierarchical query:
Data:
create table temp
(
Pos int
,Child nvarchar(18)
,Parent nvarchar(18)
,Test int
);
insert into temp (Pos, Child, Parent, Test)
values
(1, 'A', NULL, 1),
(2, 'J', NULL, 10),
(3, 'P', NULL, 16),
(4, 'Y', NULL, 25),
(1, 'B', 'A', 2),
(2, 'E', 'A', 5),
(1, 'C', 'B', 3),
(2, 'D', 'B', 4),
(1, 'F', 'E', 6),
(2, 'G', 'E', 7),
(1, 'H', 'G', 8),
(2, 'I', 'G', 9),
(1, 'K', 'J', 11),
(2, 'L', 'J', 12),
(3, 'M', 'J', 13),
(1, 'N', 'M', 14),
(2, 'O', 'M', 15),
(5, 'Z', NULL, 26),
(1, 'Q', 'P', 17),
(2, 'S', 'P', 19),
(3, 'T', 'P', 20),
(4, 'X', 'P', 24),
(1, 'R', 'Q', 18),
(1, 'U', 'T', 21),
(2, 'V', 'T', 22),
(3, 'W', 'T', 23)
Column Test is only to see at the end if the data are correctly ordered
My code so far:
declare #sql nvarchar(max);
declare #tlp nvarchar(max); --top level parents
declare #i nvarchar(4);
declare #j nvarchar(4);
declare #l nvarchar(4); --level
set #tlp = ';with tlp as (
select ROW_NUMBER() over (order by Pos) as j, * from temp where Parent IS NULL
)';
set #i = 1;
set #j = (select COUNT(*) as j from temp where Parent IS NULL);
set #sql = #tlp;
while #i < #j
begin
set #l = 1;
set #sql += '
select ' + #l + ' as Level, * from tlp where j = ' + #i
set #l = #l + 1
set #sql += '
union all
select ' + #l + ' as Level, ROW_NUMBER() over (order by Pos), * from temp where Parent = (select Child from tlp where j = ' + #i + ')'
set #i = #i + 1
if #i < #j set #sql += '
union all'
end;
exec(#sql);
Output:
level j Pos Child Parent Test
1 1 1 A NULL 1
2 1 1 B A 2
2 2 2 E A 5
1 2 2 J NULL 10
2 1 1 K J 11
2 2 2 L J 12
2 3 3 M J 13
1 3 3 P NULL 16
2 1 1 Q P 17
2 2 2 S P 19
2 3 3 T P 20
2 4 4 X P 24
1 4 4 Y NULL 25
How can I excend the query to dynamicaly go through all the childs? This is the desired output:
Level j Pos Child Parent Test
1 1 1 A NULL 1
2 1 1 B A 2
3 1 1 C B 3
3 2 2 D B 4
2 2 2 E A 5
3 1 1 F E 6
3 2 2 G E 7
4 1 1 H G 8
4 2 2 I G 9
1 2 2 J NULL 10
2 1 1 K J 11
2 2 2 L J 12
2 3 3 M J 13
3 1 1 N M 14
3 2 2 O M 15
1 3 3 P NULL 16
2 1 1 Q P 17
3 1 1 R Q 18
2 2 2 S P 19
2 3 3 T P 20
3 1 1 U T 21
3 2 2 V T 22
3 3 3 W T 23
3 4 4 X P 24
1 4 4 Y NULL 25
1 5 5 Z NULL 26
Here is a visual interpretation what I was trying to achieve:

I don't see the need for dynamic SQL at all. You have hierarchical data that you want to traverse depth-first: in SQL, this is typically done with a recursive query.
To manage the ordering of the rows, you can keep track of the path to each node.
Consider:
with cte as (
select t.*, 1 lvl, cast(child as nvarchar(max)) path
from temp t
where parent is null
union all
select t.*, c.lvl + 1, c.path + '/' + cast(t.child as nvarchar(max))
from cte c
inner join temp t on t.parent = c.child
)
select * from cte order by path
Demo on DB Fiddle:
Pos | Child | Parent | Test | lvl | path
--: | :---- | :----- | ---: | --: | :------
1 | A | null | 1 | 1 | A
1 | B | A | 2 | 2 | A/B
1 | C | B | 3 | 3 | A/B/C
2 | D | B | 4 | 3 | A/B/D
2 | E | A | 5 | 2 | A/E
1 | F | E | 6 | 3 | A/E/F
2 | G | E | 7 | 3 | A/E/G
1 | H | G | 8 | 4 | A/E/G/H
2 | I | G | 9 | 4 | A/E/G/I
2 | J | null | 10 | 1 | J
1 | K | J | 11 | 2 | J/K
2 | L | J | 12 | 2 | J/L
3 | M | J | 13 | 2 | J/M
1 | N | M | 14 | 3 | J/M/N
2 | O | M | 15 | 3 | J/M/O
3 | P | null | 16 | 1 | P
1 | Q | P | 17 | 2 | P/Q
1 | R | Q | 18 | 3 | P/Q/R
2 | S | P | 19 | 2 | P/S
3 | T | P | 20 | 2 | P/T
1 | U | T | 21 | 3 | P/T/U
2 | V | T | 22 | 3 | P/T/V
3 | W | T | 23 | 3 | P/T/W
4 | X | P | 24 | 2 | P/X
4 | Y | null | 25 | 1 | Y
5 | Z | null | 26 | 1 | Z
If a path may have more than 100 nodes, then you need to add option(maxrecursion 0) at the end of the query, otherwise you will hit the maximum level of recursion that SQL Server allows by default.

You could find material on what you are asking by searching material on Recursive Query like articles or older answers.
For creating your Recursive Query, you create a CTE where the first table is your anchor like your first level where there is column Parent is NULL. In the same CTE, you keep adding 1 to the level. Please find the answer in Fiddle
WITH MyCTE AS (
SELECT *, 1 AS Level
FROM temp
WHERE Parent IS NULL
UNION ALL
SELECT t.Pos, t.Child, t.Parent, t.Test, MyCTE.Level+1 AS Level
FROM temp AS t
INNER JOIN MyCTE
ON t.Parent = MyCTE.Child
WHERE t.Parent IS NOT NULL)
SELECT MyCTE.*, CASE WHEN Offsprings.Offspring IS NULL THEN 1 ELSE Offsprings.Offspring END AS Offspring
FROM MyCTE
LEFT JOIN (
SELECT Parent, COUNT(Parent) AS Offspring
FROM temp
GROUP BY Parent)Offsprings
ON MyCTE.Child = Offsprings.Parent
ORDER BY MyCTE.Child

Seems same answer (that I thought of..) posted by other peers with great demo. However, following example and this post may help for simple and better understanding on Recursive CTE
DDL
create table temp
(
recid int identity (1,1)
,Pos_ID int
,Child_Pos nvarchar(50)
,Parent_Pos nvarchar(50)
);
insert into temp (Pos_ID, Child_Pos, Parent_Pos)
values
(1, 'Super Boss', NULL),
(2, 'Boss', 'Super Boss'),
(3, 'Sr. Mangaer 1', 'Boss'),
(3, 'Sr. Mangaer 2', 'Boss'),
(3, 'Sr. Mangaer 3', 'Boss'),
(4, 'Mangaer 1', 'Sr. Mangaer 1'),
(4, 'Mangaer 2', 'Sr. Mangaer 1'),
(4, 'Mangaer 3', 'Sr. Mangaer 2'),
(4, 'Mangaer 4', 'Sr. Mangaer 2'),
(4, 'Mangaer 5', 'Sr. Mangaer 3'),
(4, 'Mangaer 6', 'Sr. Mangaer 3'),
(5, 'Emp 01', 'Mangaer 1'),
(5, 'Emp 02', 'Mangaer 1'),
(5, 'Emp 03', 'Mangaer 2'),
(5, 'Emp 04', 'Mangaer 2'),
(5, 'Emp 05', 'Mangaer 3'),
(5, 'Emp 06', 'Mangaer 3'),
(5, 'Emp 07', 'Mangaer 4'),
(5, 'Emp 08', 'Mangaer 4'),
(5, 'Emp 09', 'Mangaer 5'),
(5, 'Emp 10', 'Mangaer 5'),
(5, 'Emp 11', 'Mangaer 6'),
(5, 'Emp 12', 'Mangaer 6')
go
Recursive CTE Example
with main as (
select Child_Pos, Parent_Pos,Pos_ID, 1 as Reculevel
from temp as t1
--where Parent_Pos is not null
UNION ALL
select t2.Child_Pos, t2.Parent_Pos, t2.Pos_ID, main.Reculevel + 1
from temp as t2
join main on t2.Parent_Pos = main.Child_Pos
)
select * from main
Following Recursive CTE for your Example
with main as (
select Pos, Child, Parent, Test, 1 as RecuLevel
from temp as t1
UNION ALL
select t2.Pos, t2.Child, t2.Parent, t2.Test, RecuLevel + 1
from temp as t2
join main on t2.Parent = main.Child
)
select * from main
--option (maxrecursion 0) -- be cautious enabling this!

Related

T-SQL ordering data in hierarchy query from parents to children

I have hierarchical data with parents and children together with positions.
Sample:
create table groups
(
Child varchar(1),
Parent varchar(1),
Position int
);
insert into groups (Child, Parent, Position)
values ('A', NULL, 1),
('B', 'A', 1),
('C', 'B', 1),
('D', 'B', 2),
('E', NULL, 2),
('F', NULL, 3),
('G', 'F', 1),
('H', 'G', 1),
('I', 'G', 2),
('J', 'F', 2),
('K', 'J', 1),
('L', 'J', 2);
Each group and subgroup has it own position starting with 1, e.g.:
all parents have positions: 1 - x
all children with parent "A" have positions: 1 - x
all children with parent "N" have positions: 1 - x
I need to order the query the way that the children of the parents always come between parents ascending, for example (added levels for better imagination):
Level|Child|Parent|Position
-----+-----+------+---------
3 | A | NULL | 1
2 | B | A | 1
1 | C | B | 1
1 | D | B | 2
3 | E | NULL | 2
3 | F | NULL | 3
2 | G | F | 1
1 | H | G | 1
1 | I | G | 2
2 | J | F | 2
1 | K | J | 1
1 | L | J | 2
This is my current code:
;with cte as
(
select Child, Parent, Position
from groups
union all
select t.Child, t.Parent, t.Position
from groups t
join cte on t.Child = cte.Parent
)
select distinct *
from cte
order by Position
db<>fiddle example
Thank you
Your cte is a little messed up. Here's how I would do it:
;with cte as (
select Child, Parent, Position As ParentPosition, Position, 1 As level
from groups
where parent is null
union all
select t.Child, t.Parent, cte.ParentPosition, t.Position, level + 1
from groups AS t
join cte on t.Parent = cte.Child
)
select *
from cte
order by ParentPosition, level, Position
Your cte's anchor part was all the records in the table, where in fact it should be just the records that has no parents (hence the where parent is null).
Also, I've added a column for the parent's position to enable sorting all the children of the same parent before the next parent, and I've added a level column to help order the children by distance from the main parent.
Results:
Child Parent ParentPosition Position level
A 1 1 1
B A 1 1 2
C B 1 1 3
D B 1 2 3
E 2 2 1
F 3 3 1
G F 3 1 2
J F 3 2 2
K J 3 1 3
H G 3 1 3
I G 3 2 3
L J 3 2 3
Db<>Fiddle

Adding a calculated column after a pivot, based on an unused column from the original table

Considering this table:
create table #tmptmp ([Name] nvarchar(1), [Date] datetime2, [Count] int, [CountFailed] int);
insert into #tmptmp values
('A', '2020-01-03', 8, 2),
('A', '2020-01-05', 2, 0),
('A', '2020-02-12', 4, 1),
('A', '2020-02-13', 4, 1),
('A', '2020-03-21', 2, 1),
('A', '2020-03-25', 8, 1),
('B', '2020-01-03', 6, 3),
('B', '2020-01-05', 6, 1),
('B', '2020-02-12', 3, 0),
('B', '2020-02-13', 4, 0),
('B', '2020-03-21', 10, 4),
('B', '2020-03-25', 8, 1),
('C', '2020-01-03', 8, 3),
('C', '2020-01-05', 1, 1),
('C', '2020-02-12', 11, 0),
('C', '2020-02-13', 4, 0),
('C', '2020-03-21', 2, 0),
('D', '2020-01-03', 11, 0),
('D', '2020-01-05', 8, 0),
('D', '2020-02-12', 7, 0),
('D', '2020-02-13', 8, 1),
('D', '2020-03-21', 10, 0),
('D', '2020-03-25', 1, 0);
And the following pivot query:
SELECT [Name], [01], [02], [03] from
(
SELECT [Name], FORMAT([Date], 'MM') as [NumMonth], SUM([Count]) as [Total] from #tmptmp
group by FORMAT([Date], 'MM'), [Name]
) t
PIVOT
(
SUM([Total])
FOR [NumMonth] in ([01], [02], [03])
) as pivotTable
I get this result set:
| Name | Jan. | Feb. | Mar. |
| A | 10 | 8 | 10 |
| B | 12 | 7 | 18 |
| C | 9 | 15 | 2 |
| D | 19 | 15 | 11 |
How can I modify the pivot query so I can get another column that would contain the percentage of CountFailed per name?
| Name | Jan. | Feb. | Mar. | % Failed |
| A | 10 | 8 | 10 | 21.42 |
| B | 12 | 7 | 18 | 24.32 |
| C | 9 | 15 | 2 | 15.38 |
| D | 19 | 15 | 11 | 2.22 |
The CountFailed column isn't used at all in the original query, and the final column doesn't care about the pivot, it's simply SUM(CountFailed) / SUM(Count) * 100 grouped by Name.
That's easier done with conditional aggrgation:
select
name,
sum(case when date >= '20200101' and date < '20200201' then count else 0 end) as count_jan,
sum(case when date >= '20200201' and date < '20200301' then count else 0 end) as count_fev,
sum(case when date >= '20200301' and date < '20200401' then count else 0 end) as count_mar
100.0 * sum(countfailed) / sum(count) failed_percent
from mytable
group by name

How to efficiently find minimum associated ID after applying transitivity

I've a very large table with ID variables in two columns. I'd like to find the minimum associated ID after applying transitivity. For example, if ID1 = 1 and ID2 = 2 for one record and ID1 = 2 and ID2 = 3 for another record, I'd like the results to ultimately yield three records where the first column has an ID (1, 2, and 3) and the second column has a Min_ID (all equal to 1, in the above example).
I've determined that at most there are 6 ways IDs can be related, and I believe the below is a minimum functioning example.
Is there a more efficient way to do this?
Source
| ID1 | ID2 |
|------|-------|
| 1 | 1 |
| 2 | 2 |
| 1 | 1 |
| 1 | 2 |
| 2 | 11 |
| 11 | 13 |
| 13 | 99 |
| 99 | 1000 |
| 1000 | 97887 |
| 3 | 5 |
| 5 | 17 |
| 17 | 19 |
| 23 | 34 |
Results
| ID | Min_ID |
|-------|--------|
| 1 | 1 |
| 2 | 1 |
| 3 | 3 |
| 5 | 3 |
| 11 | 1 |
| 13 | 1 |
| 17 | 3 |
| 19 | 3 |
| 23 | 23 |
| 34 | 23 |
| 99 | 1 |
| 1000 | 1 |
| 97887 | 1 |
Functioning albeit potentially-inefficient working example:
IF OBJECT_ID('tempdb..##IDs') IS NOT NULL DROP TABLE ##IDs
GO
CREATE TABLE ##IDs
(
ID1 int
,ID2 int
);
INSERT INTO ##IDs (ID1, ID2)
VALUES
(1, 1),
(2, 2),
(1, 1),
(1, 2),
(2, 11),
(11, 13),
(13, 99),
(99, 1000),
(1000, 97887),
(3, 5),
(5, 17),
(17, 19),
(23, 34)
;
WITH t1 AS
(
SELECT
ID1
,ID2
FROM ##IDs
UNION
SELECT
ID2
,ID1
FROM ##IDs
),
t2 AS
(
SELECT
*
FROM t1
UNION
SELECT
a.ID1
,b.ID2
FROM t1 a
LEFT JOIN t1 b ON
a.ID2 = b.ID1
UNION
SELECT
b.ID2
,a.ID1
FROM t1 a
LEFT JOIN t1 b ON
a.ID2 = b.ID1
),
t3 AS
(
SELECT
*
FROM t2
UNION
SELECT
a.ID1
,b.ID2
FROM t2 a
LEFT JOIN t2 b ON
a.ID2 = b.ID1
UNION
SELECT
b.ID2
,a.ID1
FROM t2 a
LEFT JOIN t2 b ON
a.ID2 = b.ID1
),
t4 AS
(
SELECT
*
FROM t3
UNION
SELECT
a.ID1
,b.ID2
FROM t3 a
LEFT JOIN t3 b ON
a.ID2 = b.ID1
UNION
SELECT
b.ID2
,a.ID1
FROM t3 a
LEFT JOIN t3 b ON
a.ID2 = b.ID1
)
SELECT
ID1 AS [ID]
,MIN(ID2) Min_ID
FROM t4
GROUP BY
ID1
The complexity here for me was finding an appropriate stopping condition for the linking recursion. That said, it's really quite simple: stop linking when the ID links to itself.
IF OBJECT_ID('tempdb..##IDs') IS NOT NULL DROP TABLE ##IDs
GO
CREATE TABLE ##IDs
(
ID1 int
,ID2 int
);
INSERT INTO ##IDs (ID1, ID2)
VALUES
(1, 1),
(2, 2),
(1, 1),
(1, 2),
(2, 11),
(11, 13),
(13, 99),
(99, 1000),
(1000, 97887),
(3, 5),
(5, 17),
(17, 19),
(23, 34)
;
WITH t1 AS
(
SELECT
ID1
,ID2
FROM ##IDs
UNION
SELECT
ID2
,ID1
FROM ##IDs
UNION ALL
SELECT
c.ID1
,t1.ID2
FROM ##IDs c
INNER JOIN t1 ON
c.ID2 = t1.ID1
WHERE
c.ID1 <> c.ID2
UNION ALL
SELECT
t1.ID2
,c.ID1
FROM ##IDs c
INNER JOIN t1 ON
c.ID2 = t1.ID1
WHERE
c.ID1 <> c.ID2
)
SELECT
ID1 AS [ID]
,MIN(ID2) Min_ID
FROM t1
GROUP BY
ID1
;

How to Group based on Distinct value of other column

I have this input table
+--------+-----------+------------+----------+
| TaskId | OwnerName | WorkerName | Category |
+--------+-----------+------------+----------+
| 1 | Sara | Sara | 1 |
| 1 | Sara | Maya | 1 |
| 1 | Sara | Sara | 1 |
| 2 | Sara | Sara | 0 |
| 2 | Sara | Sara | 0 |
| 3 | Sam | Sam | 1 |
| 3 | Sam | Sam | 1 |
| 3 | Sam | Sam | 1 |
| 4 | Ella | Ella | 1 |
| 4 | Ella | Ella | 1 |
| 5 | Ella | Ella | 1 |
| 6 | Ella | Ella | 0 |
+--------+-----------+------------+----------+
I want to calculate how many times the owner name has in category column high (1) or low (0).
The tasks should be counted as Distinct , also if a task has owner name != worker name... the entire task will be dropped.
For example, TaskId 1 will be dropped.
Expected Output:
+-----------+------------------+-----------------+-------+------+
| OwnerName | #UniqueHighTasks | #UniqueLowTasks | %High | %Low |
+-----------+------------------+-----------------+-------+------+
| Sara | 0 | 1 | 0 | 100 |
| Sam | 1 | 0 | 100 | 0 |
| Ella | 2 | 1 | 66% | 33% |
+-----------+------------------+-----------------+-------+------+
My Attempt isn't taking into consideration that the distinct ids and that task 1 will be dropped, how to accomplish that?
Attempt:
SELECT
[OwnerName],
(100 * COALESCE(COUNT(CASE
WHEN [Category] = 1 THEN 1
END), 0) /
(COALESCE(COUNT(CASE
WHEN [Category] = 1 THEN 1
END), 0) + COALESCE(COUNT(CASE
WHEN [Category] = 0 THEN 1
END), 0))) AS %High,
(100 * COALESCE(COUNT(CASE
WHEN [Category] = 0 THEN 1
END), 0) /
(COALESCE(COUNT(CASE
WHEN [Category] = 0 THEN 1
END), 0) + COALESCE(COUNT(CASE
WHEN [Category] = 1 THEN 1
END), 0))) AS %Low,
SUM(case [Category] when 1 then 1 else 0 end) AS '#UniqueHighTasks',
SUM(case [Category] when 0 then 1 else 0 end) AS '#UniqueLowTasks'
FROM [dbo].[mytable]
Group by [OwnerName]
Try this query:
declare #t table (TaskId int, OwnerName varchar(10), WorkerName varchar(10), Category int)
insert into #t
values
(1, 'Sara','Sara', 1), (1, 'Sara','Maya', 1)
, (1, 'Sara','Sara', 1), (2, 'Sara','Sara', 0)
, (2, 'Sara','Sara', 0), (3, 'Sam','Sam', 1)
, (3, 'Sam','Sam', 1), (3, 'Sam','Sam', 1)
, (4, 'Ella','Ella', 1), (4, 'Ella','Ella', 1)
, (5, 'Ella','Ella', 1), (6, 'Ella','Ella', 0)
select
OwnerName, UniqueHighTasks, UniqueLowTasks
, [%High] = UniqueHighTasks * 100.0 / (UniqueHighTasks + UniqueLowTasks)
, [%Low] = UniqueLowTasks * 100.0 / (UniqueHighTasks + UniqueLowTasks)
from (
select
OwnerName, UniqueHighTasks = count(distinct case when Category = 1 then TaskId end)
, UniqueLowTasks = count(distinct case when Category = 0 then TaskId end)
from
#t
where
TaskId not in (select TaskId from #t where OwnerName <> WorkerName)
group by OwnerName
) t
I think this will do it
declare #T table (TaskId int, OwnerName varchar(20), WorkerName varchar(20), Category int);
insert into #T values
(1, 'Sara', 'Sara', 1 )
, (1, 'Sara', 'Maya', 1 )
, (1, 'Sara', 'Sara', 1 )
, (2, 'Sara', 'Sara', 0 )
, (2, 'Sara', 'Sara', 0 )
, (3, 'Sam', 'Sam', 1 )
, (3, 'Sam', 'Sam', 1 )
, (3, 'Sam', 'Sam', 1 )
, (4, 'Ella', 'Ella', 1 )
, (4, 'Ella', 'Ella', 1 )
, (5, 'Ella', 'Ella', 1 )
, (6, 'Ella', 'Ella', 0 );
with cte as
( select distinct t.TaskId, t.OwnerName, t.Category
from #T t
where TaskID not in (select TaskId from #T where OwnerName <> WorkerName)
)
select cte.OwnerName
, count(*) taskCount
, sum(category) as highCount
, count(*) - sum(category) as lowCount
, 100.0*sum(category)/count(*) as highPct
, 100.0*(count(*)-sum(category))/count(*) as lowPct
from cte
group by cte.OwnerName
OwnerName taskCount highCount lowCount lowPct highPct
-------------------- ----------- ----------- ----------- --------------------------------------- ---------------------------------------
Ella 3 2 1 66.666666666666 33.333333333333
Sam 1 1 0 100.000000000000 0.000000000000
Sara 1 0 1 0.000000000000 100.000000000000
Take your current query, and instead of querying the table directly, query a CTE or derived table that simply does a SELECT DISTINCT from the table.
You could try this
WITH nonDup AS (
SELECT DISTINCT [TaskId], [OwnerName], [WorkerName], [Category]
FROM mytable t1
WHERE NOT EXISTS(SELECT 1 FROM mytable t2 WHERE t1.TaskID = t2.TaskID and t2.OwnerName <> t2.WorkerName)
),
calc AS (
SELECT
OwnerName,
SUM(Category) AS UniqueHighTasks,
SUM(CASE WHEN Category = 0 THEN 1 ELSE 0 END) AS UniqueLowTasks
FROM nonDup
GROUP BY OwnerName
)
SELECT OwnerName,UniqueHighTasks,UniqueLowTasks,
CASE WHEN UniqueHighTasks+ UniqueLowTasks<> 0 THEN UniqueHighTasks*1.0/(UniqueHighTasks+ UniqueLowTasks) END AS [%High],
CASE WHEN UniqueHighTasks+ UniqueLowTasks<> 0 THEN UniqueLowTasks*1.0/(UniqueHighTasks+ UniqueLowTasks) END AS [%Low]
FROM calc

Sorting table in sql server

My table data like
id LedgerName
1 "105 AAA"
2 "102 sss"
3 "GGGG"
4 "107 BBB"
5 "BBBB"
6 "101 TTT"
i want sorting the Ledger like
6 "101 TTT"
2 "102 sss"
1 "105 AAA"
4 "107 BBB"
5 "BBBB"
3 "GGGG"
Normal Order by is not working.
i used split function to split for number based sorting in order by ..
how to fix this issue
With the data you have provided a regular order by on LedgerName is doing what you want.
Below is version that deals with data that is a bit more complicated.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
create table YourTable
(
id int,
LedgerName varchar(20)
)
insert into YourTable values
(1, '105 AAA' ),
(2, '1020 sss' ),
(3, ' ' ),
(4, null ),
(5, '0' ),
(6, '999 sss' ),
(7, '9999 sss' ),
(8, 'GGGG' ),
(9, '107 BBB' ),
(10, 'BBBB' ),
(11, '101 TTT' )
Query 1:
select id,
LedgerName
from YourTable
order by case when patindex('%[^0-9]%', isnull(LedgerName, '')+' ') = 1 then 1 else 0 end,
cast(left(LedgerName, patindex('%[^0-9]%', LedgerName+' ')-1) as int),
LedgerName
Results:
| ID | LEDGERNAME |
-------------------
| 5 | 0 |
| 11 | 101 TTT |
| 1 | 105 AAA |
| 9 | 107 BBB |
| 6 | 999 sss |
| 2 | 1020 sss |
| 7 | 9999 sss |
| 4 | (null) |
| 3 | |
| 10 | BBBB |
| 8 | GGGG |

Resources