I have a query in a stored procedure like below
select x,y from table
and the results will look like below
x y
1 a
1 b
2 a
2 b
3 a
3 b
i need to add a blank column or zeros when the value of x changes like below
x y
1 a
1 b
0 0
2 a
2 b
0 0
3 a
3 b
Can this be done by sql or since i'm using the data for birt reports can this be done with birt?
You need UNION ALL to add the extra rows, you also need to ORDER them, the DENSE_RANK is to get rid of the extra row.
here is how it could be done:
DECLARE #t table(x int, y char(1))
INSERT #t values
(1,'a'),(1,'b'),(2,'a'),
(2,'b'),(3,'a'),(3,'b')
;WITH CTE AS
(
SELECT
2 rn, x,y, x sort from #t
UNION ALL
SELECT
distinct dense_rank() over (order by x desc) rn, 0, '0', x+.1 sort
FROM #t
)
SELECT x,y
FROM CTE
WHERE rn > 1
ORDER BY sort, x
Result:
x y
1 a
1 b
0 0
2 a
2 b
0 0
3 a
3 b
This is working example:
DECLARE #DataSource TABLE
(
[x] TINYINT
,[y] CHAR(1)
);
INSERT INTO #DataSource ([x], [y])
VALUES (1, 'a')
,(1, 'b')
,(2, 'a')
,(2, 'b')
,(3, 'a')
,(3, 'b');
WITH DataSource AS
(
SELECT *
FROM #DataSource
UNION ALL
-- the NULL will be always displayed on the first position
SELECT DISTINCT [x]
,NULL
FROM #DataSource
)
SELECT IIF([Rank] = 1, 0, [x])
,IIF([Rank] = 1, 0, [x])
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY [x] ORDER BY [y]) AS [Rank]
,[x]
,[y]
FROM DataSource
) DS
ORDER BY [x]
,[Rank]
Few important notes:
the NULL values for each x will be with rank 1 always
the final result set is sorted by x and rank both
declare #t table (X varchar(1),Y varchar(1))
insert into #t(X,y) values (1,'A'),
(1,'B'),
(2,'A'),
(2,'B'),
(3,'A'),
(3,'B')
;with CTE As(
select X,Y,ROW_NUMBER()OVER(PARTITION BY X,Y ORDER BY X)RN
from #t
CROSS APPLY
(
values
('',NULL),
('',NULL)
) C(R, N)),
CTE2 AS(
Select CASE WHEN RN > 1 THEN 0 ELSE X END X ,
CASE WHEN RN > 1 THEN CAST(0 AS VARCHAR) ELSE Y END ID
,ROW_NUMBER()OVER(PARTITION BY X ORDER BY (SELECT NULL)) R
FROM CTE
)
select X,ID from cte2 where R <> 2
Related
I have a column in a table which has incremented values like:
AAA0000001
AAA0000002
... and so on
I want to find if the values stored in this column are in proper sequential order or if any value is missing in between or is deleted.
How can i achieve this?
Assuming the pattern is always: AAA[0-9][0-9][0-9][0-9][0-9][0-9][0-9], you can do this with a Tally Table.
Sample Data:
CREATE TABLE Tbl(val VARCHAR(10))
INSERT INTO Tbl VALUES
('AAA0000001'), ('AAA0000002'), ('AAA0000004'), ('AAA0000011');
val
----------
AAA0000001
AAA0000002
AAA0000004
AAA0000011
SQL Fiddle
;WITH Cte AS(
SELECT *,
num = CAST(SUBSTRING(val, 4, LEN(val) - 3) AS INT)
FROM Tbl
),
E1(N) AS(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b),
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b),
Tally(N) AS(
SELECT TOP(SELECT MAX(num) FROM Cte)
ROW_NUMBER() OVER(ORDER BY (SELECT NULL))
FROM E4
)
SELECT
N,
val = 'AAA' + RIGHT('0000000' + CAST(N AS VARCHAR(7)), 7)
FROM Tally
WHERE NOT EXISTS(
SELECT 1 FROM Cte WHERE num = N
)
RESULT
N val
-------------------- ----------
3 AAA0000003
5 AAA0000005
6 AAA0000006
7 AAA0000007
8 AAA0000008
9 AAA0000009
10 AAA0000010
Explanation:
The first CTE, named as Cte, extracts the numeric part of the strings and CASTs them to INT.
The succeeding CTEs, from E1 to Tally(N) generates a table with sequential values from 1 up to the MAX(num) - the INT return from the first CTE.
The final SELECT just checks for the non-existing num from the first CTE.
'AAA' + RIGHT('0000000' + CAST(N AS VARCHAR(7)), 7) transforms N so that it follows the pattern.
This is a Gaps problem. You can look into this article by Dwain Camps for more solutions on Gaps and Islands.
You can use ROW_NUMBER like this.
Sample Data
DECLARE #tab1 TABLE(id VARCHAR(20));
insert into #tab1 VALUES('AAA0000001'),('AAA0000002'),('AAA0000003'),('AAA0000004'),('AAA0000006'),('AAA0000007'),('AAA0000010');
Query
;WITH CTE as
(
SELECT convert(int,STUFF(id,1,3,'')) id,convert(int,STUFF(id,1,3,'')) - ROW_NUMBER()OVER(ORDER BY convert(int,STUFF(id,1,3,''))) rn
FROM #tab1
),CTE2 as
(
SELECT ROW_NUMBER()OVER(ORDER BY rn) as rn, MIN(id) series_start,MAX(id) series_end
FROM CTE
GROUP BY rn
)
SELECT C2.series_end,C1.series_start
FROM CTE2 C1
INNER JOIN CTE2 C2 ON C1.rn = C2.rn + 1;
SQL Fiddle
Explanation
Output of CTE is the difference of gaps between id values.
Output of CTE2 is the start and end of continuous series of numbers
Final Output gives the start and end of gaps within the series
Output
series_end series_start
4 6
7 10
If the schema is fixed then no need for complex queries. This works:
DECLARE #t TABLE ( v VARCHAR(100) );
INSERT INTO #t
VALUES ( 'AAA0000001' ),
( 'AAA0000002' ),
( 'AAA0000007' ),
( 'AAA0000008' ),
( 'AAA0000010' ),
( 'AAA0000011' ),
( 'AAA0000012' );
SELECT * FROM #t t1
CROSS APPLY(SELECT TOP 1 v FROM #t t2 WHERE t2.v > t1.v ORDER BY v) ca
WHERE RIGHT(t1.v, 7) <> RIGHT(ca.v, 7) - 1
Output:
v v
AAA0000002 AAA0000007
AAA0000008 AAA0000010
In sqlserver 2012, you can use LAG and LEAD
DECLARE #t table(col1 varchar(15))
INSERT #t values('AAA0000001'),('AAA0000002'),('AAA0000004')
SELECT
case when
stuff(lag(col1) over (order by col1), 1,3,'') + 1
= stuff(col1, 1,3,'') then 'Yes' else 'No' end previous_exists,
case when
stuff(lead(col1) over (order by col1), 1,3,'') - 1
= stuff(col1, 1,3,'') then 'Yes' else 'No' end next_exists,
col1
FROM #t
Result:
previous_exists next_exists col1
No Yes AAA0000001
Yes No AAA0000002
No No AAA0000004
I have create a function in sql to get a serial number in hierarchy.I have table called Goals.the structure of table is below
GoalId ParentId Goalstatement
---------- ---------- ----------
1 0 abc
2 0 def
3 1 acc
4 2 efc
5 3 dec
6 0 efc
7 3 jhg
I want to write a function to get the result as
Serial no GoalId ParentId GoalStatement
---------- ---------- ---------- --------------------
1 1 0
2 2 0
3 6 0
1.1 3 1
1.1.1 5 3
1.1.2 7 3
2.1 4 2
----------
I have tried with common table expression
WITH Hierarchy(GoalID, ParentId, Parents)
AS
(
SELECT GoalID, GoalParentID, CAST('' AS VARCHAR(MAX))
FROM Goals AS FirtGeneration
WHERE GoalParentID =0
UNION ALL
SELECT NextGeneration.GoalID, NextGeneration.GoalParentID,
CAST(CASE WHEN Parent.Parents = ''
THEN(CAST(NextGeneration.GoalParentID AS VARCHAR(MAX)))
ELSE(Parent.Parents + '.' + CAST(NextGeneration.GoalParentID AS VARCHAR(MAX)))
END AS VARCHAR(MAX))
FROM Goals AS NextGeneration
INNER JOIN Hierarchy AS Parent ON NextGeneration.GoalParentID = Parent.GoalID
)
SELECT *
FROM Hierarchy
OPTION(MAXRECURSION 32767)
Can any one help me to write a function to create serial number in a hierarchical way
Your recursive CTE is quite close but you need to add in a ROW_NUMBER() in order to generate the sequential numbers at each level of the hierarchy. Try this;
DECLARE #Goals TABLE (GoalId INT, GoalParentID INT, Goalstatement VARCHAR(100))
INSERT #Goals VALUES
(1, 0, 'abc'),
(2, 0, 'def'),
(3, 1, 'acc'),
(4, 2, 'efc'),
(5, 3, 'dec'),
(6, 0, 'efc'),
(7, 3, 'jhg')
;WITH NumberedGoals(GoalId, GoalParentID, Goalstatement, GoalSequence) AS (
SELECT
GoalId, GoalParentID, Goalstatement, ROW_NUMBER() OVER (PARTITION BY GoalParentID ORDER BY GoalId) AS GoalSequence
FROM
#Goals
), Hierarchy(GoalID, GoalParentID, GoalSequence, Parents)
AS
(
SELECT GoalID, GoalParentID, GoalSequence, CAST(GoalSequence AS VARCHAR(MAX))
FROM NumberedGoals AS FirtGeneration
WHERE GoalParentID = 0
UNION ALL
SELECT NextGeneration.GoalID, NextGeneration.GoalParentID, NextGeneration.GoalSequence,
CAST(CASE WHEN Parent.Parents = ''
THEN(CAST(NextGeneration.GoalSequence AS VARCHAR(MAX)))
ELSE(Parent.Parents + '.' + CAST(NextGeneration.GoalSequence AS VARCHAR(MAX)))
END AS VARCHAR(MAX))
FROM NumberedGoals AS NextGeneration
INNER JOIN Hierarchy AS Parent ON NextGeneration.GoalParentID = Parent.GoalID
)
SELECT h.Parents as [Serial no], h.GoalId, h.GoalParentId, g.GoalStatement
FROM Hierarchy h
JOIN #Goals g ON g.GoalID = h.GoalID
OPTION (MAXRECURSION 32767)
;with Hierarchy
as
(
select GoalID,
ParentId,
Row_Number() over(partition by ParentId order by GoalID) as number,
cast(Row_Number() over(partition by ParentId order by GoalID) as nvarchar(200)) newnumber
from Goals where ParentId = 0
Union All
Select p.GoalId,
p.ParentId,
Row_Number() over(partition by p.ParentId order by p.GoalID) as number,
cast(cte.newnumber + '.' + cast(Row_Number() over(partition by p.ParentId order by p.GoalID) as nvarchar(200)) as nvarchar(200)) newnumber
From Goals p
Join Hierarchy cte On cte.GoalId = p.ParentId
)
select * from Hierarchy
i have a piece of code looks like this
declare #t table (record int,string varchar(MAX))
insert into #t (record,string)values (1,'ABC')
insert into #t (record,string)values (2,'DEF/123')
insert into #t (record,string)values (3,'GHI/456/XYZ')
i got a query where i can result like this
SELECT record,
RIGHT(LEFT(T.string,Number-1),CHARINDEX('/',REVERSE(LEFT('/' + T.string,number-1))))
FROM
master..spt_values,
#t T
WHERE
Type = 'P' AND Number BETWEEN 1 AND LEN(T.string)+1
AND
(SUBSTRING(T.string,Number,1) = '/' OR SUBSTRING(T.string,Number,1) = '')
getting output
record values
1 ABC
2 DEF
2 123
3 GHI
3 456
3 XYZ
how can i get output like this
record values
1 ABC
1 NULL
1 NULL
2 DEF
2 123
2 NULL
3 GHI
3 456
3 XYZ
it has been asked by some user .i excelled upto here and from there how can i achieve desire output
The idea is to generate a rows of record cross joined to 1,2,3 to produce combination of record with another column numbered 1,2,3, then use that combination to join to your splitted values. You must add a ROW_NUMBER for your splitted values first to be able to join it with the generated combinations.
;WITH CteThree(record, N) AS(
SELECT
t.record,
x.N
FROM (
SELECT DISTINCT record FROM #t
)t
CROSS JOIN(
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
)x(N)
),
CteSplitted AS(
SELECT
record,
ROW_NUMBER() OVER(PARTITION BY record ORDER BY Number) AS N,
RIGHT(LEFT(T.string,Number-1),CHARINDEX('/',REVERSE(LEFT('/' + T.string,number-1)))) AS str
FROM master..spt_values v
CROSS JOIN #t T
WHERE
Type = 'P'
AND Number BETWEEN 1 AND LEN(T.string)+1
AND (SUBSTRING(T.string,Number,1) = '/' OR SUBSTRING(T.string,Number,1) = '')
)
SELECT
t.record,
s.str
FROM CteThree t
LEFT JOIN CteSplitted s
ON s.record = t.record
AND s.N = t.N
how about this:
declare #t table (record int,string varchar(MAX));
declare #s char(1) = '/';
WITH counter as (
SELECT MAX(LEN(string)-LEN(REPLACE(string, #s, ''))) lines
) ,
splitter as (
SELECT record, string
, line = 1
, pos = h.pos
, value = CASE WHEN h.pos>0 THEN SUBSTRING(string,1,h.pos) ELSE string END
FROM #t
CROSS APPLY (SELECT CHARINDEX(#s, string) pos ) h
UNION ALL
SELECT record, string
, line = s.line + 1
, pos = CASE WHEN s.pos = 0 THEN 0 ELSE h.pos END
, value = CASE WHEN s.pos = 0 THEN null
WHEN h.pos > 0 THEN SUBSTRING(string,s.pos+1,h.pos-s.pos-1)
ELSE SUBSTRING(string,s.pos+1,99)
END
FROM splitter s
CROSS APPLY (SELECT CHARINDEX(#s, string, s.pos+1) pos ) h
WHERE s.line<=(SELECT lines FROM counter)
)
SELECT *
FROM splitter
ORDER BY record,line
try this
DECLARE #t TABLE
(
record INT ,
string VARCHAR(MAX)
)
INSERT INTO #t
( record, string )
VALUES ( 1, 'ABC' ),
( 2, 'DEF/123' ),
( 3, 'GHI/456/XYZ' );
WITH cte
AS ( SELECT Number = 1
UNION ALL
SELECT Number + 1
FROM cte
WHERE Number <= 100
),
NotNull
AS ( SELECT record ,
RIGHT(LEFT(T.string, Number - 1),
CHARINDEX('/',
REVERSE(LEFT('/' + T.string,
number - 1)))) string ,
ROW_NUMBER() OVER ( PARTITION BY T.record ORDER BY T.record ) AS RN
FROM cte
JOIN #t T ON Number <= ( LEN(T.string) + 1 )
AND SUBSTRING(T.string + '/', Number, 1) = '/'
)
SELECT template.record ,
NotNull.string
FROM ( SELECT *
FROM ( SELECT DISTINCT
RN
FROM NotNull
) AS A
CROSS JOIN ( SELECT Record
FROM NotNull
) AS B
) AS template
LEFT JOIN NotNull ON template.RN = NotNull.RN
AND template.Record = NotNull.Record
try this
declare #t table (record int,string varchar(MAX))
insert into #t (record,string)values (1,'ABC')
insert into #t (record,string)values (2,'DEF/123')
insert into #t (record,string)values (3,'GHI/456/XYZ')
declare #mx int
select #mx= len(string)-len(replace(string,'/','')) from #t
select record,t.c.value('.','varchar(max)') as col2 from
(select record,x=cast('<t>'+replace(left(string+'////////////////////',(len(string)+(#mx-(len(string)-len(replace(string,'/','')))))),'/','</t><t>') +'</t>' as xml) from #t)
a cross apply x.nodes('/t') t(c)
I am using mssql 2008 R2,
i have below structure
create table #temp (
product int,
[order] int,
ord_qnty int
)
insert #temp
select 10 ,3,4
now, if ord_qnty is 4 , i want to select same product,order four times but in all four rows thevalue of ord_qnty should be 1 , i.e.
out put should be
Product order ord_qnty
10 3 1
10 3 1
10 3 1
10 3 1
If you have a numbers table, you can use that. If not, you can generate one:
;with Numbers(n) as (
select ROW_NUMBER() OVER (ORDER BY object_id) from sys.objects
)
select product,[order],1 as ord_qnty
from #temp t inner join Numbers num
on t.ord_qnty >= num.n
(In my nearly empty scratch database, the ROW_NUMBER() generates 77 rows. If that's not going to be enough, you can introduce cross-joins or use other tricks to generate more numbers, or you can create and populate a permanent numbers table)
Try this one -
Query:
DECLARE #temp TABLE
(
product INT
, [order] INT
, ord_qnty INT
)
INSERT #temp(product, [order], ord_qnty)
SELECT 10, 3, 4
SELECT
t.product
, t.[order]
, ord_qnty = 1
FROM #temp t
JOIN [master].dbo.spt_values sv ON t.ord_qnty > sv.number
WHERE sv.[type] = 'p'
SELECT
t.product
, t.[order]
, ord_qnty = 1
FROM #temp t
JOIN (
SELECT number = ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM sys.system_parameters p
) sv ON t.ord_qnty >= sv.number
Output:
product order ord_qnty
----------- ----------- -----------
10 3 1
10 3 1
10 3 1
10 3 1
Query Cost:
For any "millions value":
SET NOCOUNT ON;
DECLARE #numbers TABLE (number INT)
DECLARE #temp TABLE
(
product INT
, [order] INT
, ord_qnty INT
)
INSERT #temp(product, [order], ord_qnty)
SELECT 10, 3, 4
DECLARE
#i BIGINT = 1
, #max BIGINT = (
SELECT MAX(ord_qnty)
FROM #temp
)
WHILE (#i <= #max) BEGIN
INSERT INTO #numbers (number)
VALUES (#i), (#i+1), (#i+2), (#i+3), (#i+4), (#i+5), (#i+6), (#i+7), (#i+8), (#i+9)
SELECT #i += 10
END
SELECT
t.product
, t.[order]
, ord_qnty = 1
FROM #temp t
CROSS JOIN (
SELECT *
FROM #numbers
WHERE number < #max + 1
) t2
I have following table:
ID ParentID
1 NULL
2 1
3 2
4 NULL
5 4
6 5
7 3
I want to find the first ID of a specific child ID.
Example: ID=7 and the result is 1
ID=6 and the result is 4
How to do it?
You need to do a bit of recursive CTE magic to solve this one.
Given the data in a table variable thusly:
declare #data table(id int, parentid int)
insert into #data
select 1, null
union select 2,1
union select 3,2
union select 4, null
union select 5,4
union select 6,5
union select 7,3
The following should do the trick:
;with recursiveTable as
(
select d.id, d.parentId, 0 as depth
from #data d
where d.id=6 -- Parameterize this
union all
select d.id, d.parentid, r.depth-1
from #data d
inner join recursiveTable r
on d.id = r.parentId
)
select top 1 id
from recursiveTable
order by depth
Plugging 6 in as above returns 4. Changing this to 7 returns 1 as requested.
Try this:
CREATE TABLE childpar(ID int,ParentID int)
INSERT INTO childpar
values(1,NULL),
(2, 1),
(3, 2),
(4, NULL),
(5, 4),
(6, 5),
(7, 3)
DECLARE #inpyID int
SET #inpyID=7
;WITH CTE1 as (
select * from childpar where id=#inpyID
union all
select c2.* from CTE1 c1 inner join childpar c2 on c1.ParentID = c2.ID
)
select top 1 id from CTE1 order by id asc