I have a query that returns something like this:
Id | Value
1 | Hi,
1 | I'm
2 | just
2 | an
2 | example
3 | message.
What I want to do is number the rows based on the id. So with the example above, I want to return something like this:
Id | Value | Number
1 | Hi, | 1
1 | I'm | 2
2 | just | 1
2 | an | 2
2 | example | 3
3 | message. | 1
Is there a simple way to do this in the same query as the one you would use in the first example?
The problem is there is NOTHING in that table that guarantees the order of rows, so the result you want cannot be guaranteed.
select *
, row_number() over(partition by id order by (select 1)) as Number
from yourtable
You can make use of the rownumber function.
SELECT ROW_NUMBER OVER (PARTITION BY ID ORDER BY (SELECT 1)) AS RowNumb
You Can Use the ROW_NUMBER() Function and Partition by Id. Like this
DECLARE #MyTable AS TABLE
(
Id INT,
[Value] VARCHAR(50)
)
INSERT INTO #MyTable
(
Id,
[Value]
)
SELECT '1','Hi,' UNION
SELECT '1','I''m' UNION
SELECT '2','just' UNION
SELECT '2','an' UNION
SELECT '2','example' UNION
SELECT '3','message.'
SELECT
*,
Number = ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Id)
FROM #MyTable
Result
Id Value Number
----------- -------------------------------------------------- --------------------
1 Hi, 1
1 I'm 2
2 an 1
2 example 2
2 just 3
3 message. 1
Please try following script.
DECLARE #MyTable AS TABLE
(
Id INT,
[Value] VARCHAR(50)
)
INSERT INTO #MyTable
(
Id,
[Value]
)
SELECT '1','Hi,' UNION
SELECT '1','I''m' UNION
SELECT '2','just' UNION
SELECT '2','an' UNION
SELECT '2','example' UNION
SELECT '3','message.'
select *, dense_rank()over(partition by Id order by Value) as Number from #MyTable
/*
Id Value Number
----------- -------------------------------------------------- --------------------
1 Hi, 1
1 I'm 2
2 an 1
2 example 2
2 just 3
3 message. 1
*/
Best Regards,
Rachel
Related
I have a non-normalized table with several columns. I would like to return all columns that have a positive number along with a negative number of the same value.
Example:
ID | Value
-------------
1 | 10
1 | -10
3 | 15
3 | 15
4 | -1
5 | 4
Current Output:
ID | Values
-------------
1 | 10
1 | -10
3 | 15
3 | 15
Desired Output:
ID | Value
-------------
1 | 10
1 | -10
I have made a windows function as seen below that will select absolute values that are the same, but this includes pairs where there are a positive number.
select Count(*) Over (Partition By DVN, [Tran Date], [Reference Number],Description,Vendor, Abs([Maintenance Expense])) As cnt , *
From WorkTemp.dbo.Customer2700Combine
Where [Maintenance Expense] Is Not Null
Order By 1 Desc,DVN, [Tran Date], [Reference Number],Description,Vendor, Abs([NonRental Total])
Not sure if your requirement is by [ID], looking at your example, description and desired output, this is how I would do it:
DROP TABLE IF EXISTS #sopg;
SELECT [ID],
[VALUE]
INTO #sopg
FROM
(
SELECT 1 AS ID,
10 AS VALUE
UNION
SELECT 1 AS ID,
-10 AS VALUE
UNION
SELECT 3 AS ID,
15 AS VALUE
UNION
SELECT 3 AS ID,
15 AS VALUE
UNION
SELECT 4 AS ID,
-1 AS VALUE
UNION
SELECT 5 AS ID,
4 AS VALUE
) x;
-- Assuming that one ID can only have maximum 2 rows (like your example above) and want this by ID
SELECT s.[ID],
s.[VALUE]
FROM #sopg s
INNER JOIN
(
SELECT ID,
SUM(VALUE) SumZero
FROM #sopg
GROUP BY ID
HAVING SUM(VALUE) = 0
) SumZero ON SumZero.ID = s.ID
-- Another way, assuming that ID can have more than 2 rows and different values
DROP TABLE IF EXISTS #sopg2;
SELECT [ID],
[VALUE]
INTO #sopg2
FROM
(
SELECT 1 AS ID,
10 AS VALUE
UNION
SELECT 1 AS ID,
-10 AS VALUE
UNION
SELECT 1 AS ID,
-9 AS VALUE
UNION
SELECT 3 AS ID,
15 AS VALUE
UNION
SELECT 3 AS ID,
15 AS VALUE
UNION
SELECT 4 AS ID,
-1 AS VALUE
UNION
SELECT 5 AS ID,
4 AS VALUE
) x
SELECT a.[ID],
a.[VALUE]
FROM #sopg2 a
INNER JOIN #sopg b ON b.ID = a.ID AND a.VALUE = -b.VALUE
Brothers can you help me? Thanks
Table A
Id Name IdParent
1 Operation Null
2 Developer 1
3 Android 2
4 IOS 2
Expectes result:
ID Name
1 +Operation
2 +------ Developer
3 +------------Android
4 +------------ IOS
By adding a sequence during the recursive build, you can easily create the proper presentation sequence and nesting
Declare #YourTable table (id int,IdParent int,Name varchar(50))
Insert into #YourTable values
( 1, NULL,'Operation')
,( 2, 1 ,'Developer')
,( 3, 2 ,'Android')
,( 4, 2 ,'IOS')
,( 5, 1 ,'Poet')
,( 6, 5 ,'Limerick')
,( 7, 5 ,'Haiku')
Declare #Top int = null --<< Sets top of Hier Try 2
Declare #Nest varchar(25) = '|-----' --<< Optional: Added for readability
;with cteP as (
Select Seq = cast(10000+Row_Number() over (Order by Name) as varchar(500))
,ID
,IdParent
,Lvl=1
,Name
From #YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(IdParent ,-1) else ID end
Union All
Select Seq = cast(concat(p.Seq,'.',10000+Row_Number() over (Order by r.Name)) as varchar(500))
,r.ID
,r.IdParent
,p.Lvl+1
,r.Name
From #YourTable r
Join cteP p on r.IdParent = p.ID)
Select A.ID
,A.IdParent
,A.Lvl
,Name = Replicate(#Nest,A.Lvl-1) + A.Name
From ctep A
Order By A.Seq
Returns
ID IdParent Lvl Name
1 NULL 1 Operation
2 1 2 |-----Developer
3 2 3 |-----|-----Android
4 2 3 |-----|-----IOS
5 1 2 |-----Poet
7 5 3 |-----|-----Haiku
6 5 3 |-----|-----Limerick
Here's another version:
WITH RawData AS (
SELECT 1 AS Id, 'Operation' AS Name, CONVERT(INT, NULL) AS IdParent
UNION ALL
SELECT 2 AS Id, 'Developer' AS Name, 1 AS IdParent
UNION ALL
SELECT 3 AS Id, 'Android' AS Name, 2 AS IdParent
UNION ALL
SELECT 4 AS Id, 'IOS' AS Name, 2 AS IdParent),
Depth AS (
SELECT
Id,
1 AS depth,
IdParent
FROM
RawData
UNION ALL
SELECT
d.Id,
d.depth + 1,
r.IdParent
FROM
Depth d
INNER JOIN RawData r ON r.Id = d.IdParent),
MaxDepth AS (
SELECT
Id,
MAX(depth) AS depth
FROM
Depth
GROUP BY
Id)
SELECT
r.Id,
'+' + REPLICATE('----', m.depth - 1) + r.Name AS Name
FROM
RawData r
INNER JOIN MaxDepth m ON m.Id = r.Id;
Results:
Id Name
1 +Operation
2 +----Developer
3 +--------Android
4 +--------IOS
DECLARE #mockup TABLE(Id INT, Name VARCHAR(100), IdParent INT);
INSERT INTO #mockup VALUES
(1,'Operation',Null)
,(2,'Developer',1)
,(3,'Android',2)
,(4,'IOS',2);
--The query uses a recursive CTE and finally REPLICATE with the recursive level to add the number of hyphens...
WITH recCTE AS
(
SELECT Id, Name, 1 AS Lvl, CAST(REPLACE(STR(ROW_NUMBER() OVER (ORDER BY Id),5),' ','0') AS VARCHAR(MAX)) AS Seq
FROM #mockup
WHERE IdParent IS NULL
UNION ALL
SELECT m.Id,m.Name,r.Lvl +1,r.Seq + '.' + REPLACE(STR(ROW_NUMBER() OVER (ORDER BY m.Id),5),' ','0')
FROM #mockup AS m
INNER JOIN recCTE AS r ON m.IdParent=r.Id
)
SELECT *
,'+' + REPLICATE('-',Lvl*4) + Name
FROM recCTE
ORDER BY Seq
the result
+----+-----------+-----+----------------------+
| Id | Name | Lvl | (Kein Spaltenname) |
+----+-----------+-----+----------------------+
| 1 | Operation | 1 | +----Operation |
+----+-----------+-----+----------------------+
| 2 | Developer | 2 | +--------Developer |
+----+-----------+-----+----------------------+
| 3 | Android | 3 | +------------Android |
+----+-----------+-----+----------------------+
| 4 | IOS | 3 | +------------IOS |
+----+-----------+-----+----------------------+
I have the following Table definition with sample data. In the following table.
"TP" consecutive 3 records 2 times,then "SL" consecutive 1 records 2 times……
id | Result
1 | TP
2 | TP
3 | TP
4 | SL
5 | TP
6 | NONE
7 | NONE
8 | SL
9 | TP
10 | TP
11 | TP
12 | SL
13 | SL
14 | SL
And I am looking for a result like this:
comboNum | num
TP_3 | 2
SL_1 | 2
TP_1 | 1
SL_3 | 1
Any suggestions?
You can as the below
DECLARE #Tbl TABLE (Id INT, Result VARCHAR(10))
INSERT INTO #Tbl
VALUES
(1,'TP')
,(2,'TP')
,(3,'TP')
,(4,'SL')
,(5,'TP')
,(6,'NONE')
,(7,'NONE')
,(8,'SL')
,(9,'TP')
,(10,'TP')
,(11,'TP')
,(12,'SL')
,(13,'SL')
,(14,'SL')
;WITH CTE1
AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY Result, Id) RowId FROM #Tbl
),CTE2
AS
(
SELECT
Result,
MAX(C.Id) - MIN(C.Id) Cons,
MIN(C.Id) StartP,
MAX(C.Id) EndP
FROM
CTE1 C
WHERE
c.Result <> 'NONE'
GROUP BY
C.Result,
C.RowId - C.Id
)
SELECT
C.Result + '_' + CAST(C.Cons + 1 AS VARCHAR(50)) AS comboNum,
COUNT(*) AS Num
FROM
CTE2 C
GROUP BY
C.Result,
C.Cons
ORDER BY Num DESC
Result:
comboNum Num
------------------ -----------
TP_3 2
SL_1 2
TP_1 1
SL_3 1
Two CTEs with tricky ROW_NUMBER() sequence:
;WITH cte as (
SELECT id,
Result,
ROW_NUMBER() OVER (PARTITION BY Result ORDER BY id) - ROW_NUMBER() OVER (ORDER BY id) as seq
FROM YourTable
WHERE Result != 'NONE'
), final AS (
SELECT MIN(id) as mid,
Result +'_'+ CAST(MAX(id)-MIN(id)+1 as nvarchar(max)) as comboNum
FROM cte
GROUP BY Result, seq
)
SELECT comboNum,
COUNT(mid) as num
FROM final
GROUP BY comboNum
ORDER BY MIN(mid)
Output:
comboNum num
TP_3 2
SL_1 2
TP_1 1
SL_3 1
Declare #tblTest AS TABLE(
ID INT,
Result VARCHAR(50)
)
INSERT INTO #tblTest VALUES(1,'TP')
,(2,'TP')
,(3,'TP')
,(4,'SL')
,(5,'TP')
,(6,'NONE')
,(7,'NONE')
,(8,'SL')
,(9,'TP')
,(10,'TP')
,(11,'TP')
,(12,'SL')
,(13,'SL')
,(14,'SL')
;WITH X AS
(
SELECT
T.*,
ROW_NUMBER() OVER (ORDER BY ID) AS SrNo,
ROW_NUMBER() OVER (PARTITION BY Result ORDER BY id) AS PartNo
FROM #tblTest T
WHERE Result<>'NONE'
)
SELECT
ComboNum,
COUNT(Occurance) AS Num
FROM
(
SELECT
Result +'_'+ CAST((max(ID)-min(ID))+1 AS VARCHAR(5)) AS ComboNum,
(MAX(ID)-MIN(ID))+1 AS Occurance,
MIN(SrNo) AS SrNo
FROM X
GROUP BY Result, (SrNo - PartNo)
) Z
GROUP BY ComboNum,Occurance
ORDER BY MIN(SrNo)
Output:
I have an audit table, part of data is looked like:
Id ColumnName Value RowId
---------------------------------
1 EditCheckId 100 1
2 FieldData_Id 10 1
3 EditType 1 1
4 Outcome True 1
5 EditCheckId 200 2
6 FieldData_Id 20 2
7 EditType 2 2
8 Outcome False 2
9 EditCheckId 300 3
10 FieldData_Id 30 3
11 EditType 3 3
12 Outcome True 3
and I want to construct such table groupping data by RowId
EditCheck_Id FieldData_Id EditType Outcome
---------------------------------------------------
100 10 1 True
200 20 2 False
300 30 3 True
I've tried the query:
select [EditCHeck_Id], [FieldData_Id], [EditType], [Outcome]
from
(
select [ColumnName], [Value]
from Audit a
) as [SourceTable]
pivot
(
max([Value])
for [ColumnName] in ([EditCHeck_Id], [FieldData_Id], [EditType], [Outcome])
) as [PivotTable];
http://sqlfiddle.com/#!6/7af71/3
using PIVOT statement but there is only one row in answer. Where is my problem?
You'll need some value to GROUP BY to make each of the rows distinct. Typically, you would use a windowing function like row_number() to generate a unique sequenced number over your current columnname. You can alter your query to the following:
select
[EditCheckId],
[FieldData_Id],
[EditType],
[Outcome]
from
(
select [ColumnName], [Value],
rn = row_number() over(partition by ColumnName order by id)
from Audit a
) as [SourceTable]
pivot
(
max([Value])
for [ColumnName] in ([EditCheckId], [FieldData_Id], [EditType], [Outcome])
) as [PivotTable];
See SQL Fiddle with Demo.
You could also use an aggregate function with a CASE expression to get the final result:
select
max(case when ColumnName = 'EditCheckId' then value end) [EditCheckId],
max(case when ColumnName = 'FieldData_Id' then value end) [FieldData_Id],
max(case when ColumnName = 'EditType' then value end) [EditType],
max(case when ColumnName = 'Outcome' then value end) [Outcome]
from
(
select [ColumnName], [Value],
rn = row_number() over(partition by ColumnName order by id)
from Audit a
) d
group by rn;
See SQL Fiddle with Demo Both give the result:
| EDITCHECKID | FIELDDATA_ID | EDITTYPE | OUTCOME |
|-------------|--------------|----------|---------|
| 100 | 10 | 1 | True |
| 200 | 20 | 2 | False |
| 300 | 30 | 3 | True |
You need something to distinguish the output rows, like for example your RowId
pivot does the grouping for you like so:
select [RowID], [EditCheckId], [FieldData_Id], [EditType], [Outcome]
from
(
select [ColumnName], [Value], [RowId]
from Audit a
) as [SourceTable]
pivot
(
max([Value])
for [ColumnName] in ([EditCheckId], [FieldData_Id], [EditType], [Outcome])
) as [PivotTable];
edited Fiddle demo
I have a tree struture in a single table. The table is a tree of categories that can be nested endlessly. Each category has a ProductCount column that tells how many products are directly in the category (not summing child categories).
Id | ParentId | Name | ProductCount
------------------------------------
1 | -1 | Cars | 0
2 | -1 | Bikes | 1
3 | 1 | Ford | 10
4 | 3 | Mustang | 7
5 | 3 | Focus | 4
I would like to make a sql query that for each row/category gives me the number of products including the ones in the child categories.
The output for the table above should be
Id | ParentId | Name | ProductCount | ProductCountIncludingChildren
--------------------------------------------------------------------------
1 | -1 | Cars | 0 | 21
2 | -1 | Bikes | 1 | 1
3 | 1 | Ford | 10 | 21
4 | 3 | Mustang | 7 | 7
5 | 3 | Focus | 4 | 4
I know I probably should use CTE, but cant quite get it working the way it should.
Any help is appreciated!
You can use a recursive CTE where you in the anchor part get all rows and in the recursive part join to get the child rows. Remember the original Id aliased RootID from the anchor part and do sum aggregate in the main query grouped by RootID.
SQL Fiddle
MS SQL Server 2012 Schema Setup:
create table T
(
Id int primary key,
ParentId int,
Name varchar(10),
ProductCount int
);
insert into T values
(1, -1, 'Cars', 0),
(2, -1, 'Bikes', 1),
(3, 1, 'Ford', 10),
(4, 3, 'Mustang', 7),
(5, 3, 'Focus', 4);
create index IX_T_ParentID on T(ParentID) include(ProductCount, Id);
Query 1:
with C as
(
select T.Id,
T.ProductCount,
T.Id as RootID
from T
union all
select T.Id,
T.ProductCount,
C.RootID
from T
inner join C
on T.ParentId = C.Id
)
select T.Id,
T.ParentId,
T.Name,
T.ProductCount,
S.ProductCountIncludingChildren
from T
inner join (
select RootID,
sum(ProductCount) as ProductCountIncludingChildren
from C
group by RootID
) as S
on T.Id = S.RootID
order by T.Id
option (maxrecursion 0)
Results:
| ID | PARENTID | NAME | PRODUCTCOUNT | PRODUCTCOUNTINCLUDINGCHILDREN |
|----|----------|---------|--------------|-------------------------------|
| 1 | -1 | Cars | 0 | 21 |
| 2 | -1 | Bikes | 1 | 1 |
| 3 | 1 | Ford | 10 | 21 |
| 4 | 3 | Mustang | 7 | 7 |
| 5 | 3 | Focus | 4 | 4 |
This is the same concept as Tom's answer, but less code (and way faster).
with cte as
(
select v.Id, v.ParentId, v.Name, v.ProductCount,
cast('/' + cast(v.Id as varchar) + '/' as varchar) Node
from Vehicle v
where ParentId = -1
union all
select v.Id, v.ParentId, v.Name, v.ProductCount,
cast(c.Node + CAST(v.Id as varchar) + '/' as varchar)
from Vehicle v
join cte c on v.ParentId = c.Id
)
select c1.Id, c1.ParentId, c1.Name, c1.ProductCount,
c1.ProductCount + SUM(isnull(c2.ProductCount, 0)) ProductCountIncludingChildren
from cte c1
left outer join cte c2 on c1.Node <> c2.Node and left(c2.Node, LEN(c1.Node)) = c1.Node
group by c1.Id, c1.ParentId, c1.Name, c1.ProductCount
order by c1.Id
SQL Fiddle (I added some extra data rows for testing)
Actually this could be a good use of HIERARCHYID in SQL Server..
CREATE TABLE [dbo].[CategoryTree]
(
[Id] INT,
[ParentId] INT,
[Name] VARCHAR(100),
[ProductCount] INT
)
GO
INSERT [dbo].[CategoryTree]
VALUES
(1, -1, 'Cars', 0),
(2, -1, 'Bikes', 1),
(3, 1, 'Ford', 10),
(4, 3, 'Mustang', 7),
(5, 3, 'Focus', 4)
--,(6, 1, 'BMW', 100)
GO
Query
WITH [cteRN] AS (
SELECT *,
ROW_NUMBER() OVER (
PARTITION BY [ParentId] ORDER BY [ParentId]) AS [ROW_NUMBER]
FROM [dbo].[CategoryTree]
),
[cteHierarchy] AS (
SELECT CAST(
CAST(hierarchyid::GetRoot() AS VARCHAR(100))
+ CAST([ROW_NUMBER] AS VARCHAR(100))
+ '/' AS HIERARCHYID
) AS [Node],
*
FROM [cteRN]
WHERE [ParentId] = -1
UNION ALL
SELECT CAST(
hierarchy.Node.ToString()
+ CAST(RN.[ROW_NUMBER] AS VARCHAR(100)
) + '/' AS HIERARCHYID),
rn.*
FROM [cteRN] rn
INNER JOIN [cteHierarchy] hierarchy
ON rn.[ParentId] = hierarchy.[Id]
)
SELECT x.[Node].ToString() AS [Node],
x.[Id], x.[ParentId], x.[Name], x.[ProductCount],
x.[ProductCount] + SUM(ISNULL(child.[ProductCount],0))
AS [ProductCountIncludingChildren]
FROM [cteHierarchy] x
LEFT JOIN [cteHierarchy] child
ON child.[Node].IsDescendantOf(x.[Node]) = 1
AND child.[Node] <> x.[Node]
GROUP BY x.[Node], x.[Id], x.[ParentId], x.[Name], x.[ProductCount]
ORDER BY x.[Id]
Result
This wont be optimal but it works, however it involves 2 CTEs. 1 main CTE and a CTE in a table valued function to sum up the values for each sub tree.
The first CTE
;WITH cte
AS
(
SELECT
anchor.Id,
anchor.ParentId,
anchor.Name,
anchor.ProductCount,
s.Total AS ProductCountIncludingChildren
FROM
testTable anchor
CROSS APPLY SumChild(anchor.id) s
WHERE anchor.parentid = -1
UNION ALL
SELECT
child.Id,
child.ParentId,
child.Name,
child.ProductCount,
s.Total AS ProductCountIncludingChildren
FROM
cte
INNER JOIN testTable child on child.parentid = cte.id
CROSS APPLY SumChild(child.id) s
)
SELECT * from cte
AND the function
CREATE FUNCTION SumChild
(
#id int
)
RETURNS TABLE
AS
RETURN
(
WITH cte
AS
(
SELECT
anchor.Id,
anchor.ParentId,
anchor.ProductCount
FROM
testTable anchor
WHERE anchor.id = #id
UNION ALL
SELECT
child.Id,
child.ParentId,
child.ProductCount
FROM
cte
INNER JOIN testTable child on child.parentid = cte.id
)
SELECT SUM(ProductCount) AS Total from CTE
)
GO
Which results in:
from the source table
Apologies about formatting.
I couldn't come up with a good T-SQL, set based answer, but I did come up with an answer:
The temp table mimics your table structure. The table variable is a work table.
--Initial table
CREATE TABLE #products (Id INT, ParentId INT, NAME VARCHAR(255), ProductCount INT)
INSERT INTO #products
( ID,ParentId, NAME, ProductCount )
VALUES ( 1,-1,'Cars',0),(2,-1,'Bikes',1),(3,1,'Ford',10),(4,3,'Mustang',7),(5,3,'Focus',4)
--Work table
DECLARE #products TABLE (ID INT, ParentId INT, NAME VARCHAR(255), ProductCount INT, ProductCountIncludingChildren INT)
INSERT INTO #products
( ID ,
ParentId ,
NAME ,
ProductCount ,
ProductCountIncludingChildren
)
SELECT Id ,
ParentId ,
NAME ,
ProductCount,
0
FROM #products
DECLARE #i INT
SELECT #i = MAX(id) FROM #products
--Stupid loop - loops suck
WHILE #i > 0
BEGIN
WITH cte AS (SELECT ParentId, SUM(ProductCountIncludingChildren) AS ProductCountIncludingChildren FROM #products GROUP BY ParentId)
UPDATE p1
SET p1.ProductCountIncludingChildren = p1.ProductCount + isnull(p2.ProductCountIncludingChildren,0)
FROM #products p1
LEFT OUTER JOIN cte p2 ON p1.ID = p2.ParentId
WHERE p1.ID = #i
SELECT #i = #i - 1
END
SELECT *
FROM #products
DROP TABLE #products
I'd be very interested to see a better, set based approach. The problem I ran into is that when you use recursive cte's, you start with the parent and work toward the children - this doesn't really work for getting a sum at the parent levels. You'd have to do some kind of backward recursive cte.