Right using of PIVOT-statement in SQL Server - sql-server

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

Related

Number rows based on id

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

Return rows that have a negative equivalent

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

SQL Server how to sum max for specific category?

Got a problem when constructing a analysis SQL using SQL Server
The raw data as below
GameID | UsrRegID | Score_User
281 | 1 | 1
281 | 1 | 2
281 | 1 | 3
282 | 1 | 0
282 | 1 | 0
282 | 1 | 1
283 | 1 | 2
283 | 1 | 3
Below is the expect output result:
Distinct_Count_GameID | UsrRegID | Score_User
3 | 1 | 7
The logic for calculating the Score_user as below:
Sum(Max(Score_user) for each GemeID)
So the result need to be 3+1+3=7.
Can using the pure SQL to get the above expecting output?
I think we need to aggregate twice here. One option uses ROW_NUMBER:
WITH cte AS (
SELECT GameID, UsrRegID, Score_User,
ROW_NUMBER() OVER (PARTITION BY GameID, UsrRegID ORDER BY Score_User DESC) rn
FROM yourTable
)
SELECT
UsrRegID,
COUNT(DISTINCT GameID) AS Distinct_Count_GameID,
SUM(Score_User) AS Score_User
FROM cte
WHERE rn = 1
GROUP BY
UsrRegID;
You can't do an aggregate of an aggregate on the same SELECT, you can chain them together with CTE or subqueries.
;WITH Maxs AS
(
SELECT
T.GameID,
T.UsrRegID,
MaxScore = MAX(T.Score_User)
FROM
YourTable AS T
GROUP BY
T.GameID,
T.UsrRegID
)
SELECT
M.UsrRegID,
Distinct_Count_GameID = COUNT(DISTINCT(M.GameID)),
Score_User = SUM(M.MaxScore)
FROM
Maxs AS M
GROUP BY
M.UsrRegID
You can also try like following.
SELECT Count(DISTINCT [rgameid]) Distinct_Count_GameID,
Count(DISTINCT [usrregid]) UsrRegID,
(SELECT Sum(M)
FROM (SELECT Max([score_user]) M
FROM [TableName]
GROUP BY [rgameid])t) AS Score_User
FROM [TableName]
DEMO
First find maximum value of score for each GameId and UsrRegID and then find SUM() for the column, Score_User and group it by the columns, GameID and UsrRegID using GROUP BY clause.
Query
select count(distinct [t].[GameID]) as [GameID], [t].[UsrRegID],
sum([t].[Score_User]) as [Score_User] from(
select [GameID], [UsrRegID], max([Score_User]) as [Score_User]
from [your_table_name]
group by [GameID], [UsrRegID]
) as [t]
group by [t].[UsrRegID];
Or, give a row number based on the descending order of score value and group by GameID and UsrRegID. Then find the count of distinct GameId and sum of maximum score.
Query
;with cte as(
select [rn] = row_number() over(
partition by [GameID], [UsrRegID]
order by [Score_User] desc
), *
from [your_table_name]
)
select count(distinct [GameID]) as [GameID], [UsrRegID],
sum([Score_User]) as [Score_User] from cte
where [rn] = 1
group by [UsrRegID];
Aggregates and a COUNT(Distinct GameID):
declare #raw as table (GameID int, UsrRegID int, Score_user int)
insert into #raw values (281, 1, 1)
,(281, 1, 2)
,(281, 1, 3)
,(282, 1, 0)
,(282, 1, 0)
,(282, 1, 1)
,(283, 1, 2)
,(283, 1, 3)
select count(distinct GameID) as Distinct_Count_GameID, UsrRegID, sum(max_score_user)
from
(
select GameID
, UsrRegID
, max(score_user) as max_score_user
from #raw
group by GameID, UsrRegID
) a
group by a.UsrRegID

different result Consecutive records in a table using SQL

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:

Hide rows when column values are duplicate

I have a table named "letters" with two columns looking like this:
case_nr | date
--------+-----------------------
1 | 2015-06-13 12:45:04
1 | NULL
2 | 2015-06-11 12:45:09
3 | 2015-06-12 17:41:49
3 | 2015-06-13 18:42:99
case_nr 1 have printed 2 letters but only one was sent
I want to filter all cases where all letters was sent (have a date)
So in this case result should be:
2
3
You can use DISTINCT with NOT IN:
SELECT DISTINCT case_nr
FROM TableName
WHERE case_nr NOT IN
(SELECT case_nr FROM TableName WHERE [date] IS NULL )
Result:
case_nr
--------
2
3
Sample result in SQL Fiddle.
Group by the case_nr and take only those having no record with date is null
select case_nr
from your_table
group by case_nr
having sum(case when date is null then 1 else 0 end) = 0
SQLFiddle demo
As an alternative:
SELECT
case_nr
FROM (
SELECT
case_nr,
COUNT(CASE WHEN [date] IS NULL THEN 1 END) AS cnt
FROM
yourTable
GROUP BY
case_nr
) t
WHERE
cnt = 0

Resources