Hide the same cells in SQL Server result - sql

I have a result from tables similar to this:
id | name | type
---+------+------
1 | John | 1
1 | John | 34
2 | Jane | 2
1 | John | 12
2 | Jane | 168
I need to hide repeated values and let only unique values. I need to get something like this
id | name | type
---+------+------
1 | John | 1
| | 34
| | 12
2 | Jane | 2
| | 168
How can I do that in SQL Server 2012?

This is something that your presentation layer ought to handle generally but...
WITH T AS
(
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Name, Type) AS RN1,
ROW_NUMBER() OVER (PARTITION BY ID, Name ORDER BY Type) AS RN2
FROM YourTable
)
SELECT
CASE WHEN RN1 = 1 THEN ID END AS ID,
CASE WHEN RN2 = 1 THEN Name END AS Name,
Type
FROM T
ORDER BY ID, Name, Type

If you need nullify marked rows, you can use following query:
WITH Src AS
(
SELECT * FROM (VALUES
(1, 'John', 1 ),
(1, 'John', 34 ),
(2, 'Jane', 2 ),
(1, 'John', 12 ),
(2, 'Jane', 168)
)T(id, name, [type])
)
SELECT
CASE WHEN RN=1 THEN id END id,
CASE WHEN RN=1 THEN name END name,
[Type]
FROM (SELECT *, ROW_NUMBER() OVER (PARTITION BY id, name ORDER BY [Type]) RN FROM Src) T
It returns:
id name Type
----------- ---- -----------
1 John 1
NULL NULL 12
NULL NULL 34
2 Jane 2
NULL NULL 168

Related

Condense or merge rows with null values not using group by

Let's say I have a select which returns the following Data:
select nr, name, val_1, val_2, val_3
from table
Nr. | Name | Value 1 | Value 2 | Value 3
-----+------------+---------+---------+---------
1 | Max | 123 | NULL | NULL
1 | Max | NULL | 456 | NULL
1 | Max | NULL | NULL | 789
9 | Lisa | 1 | NULL | NULL
9 | Lisa | 3 | NULL | NULL
9 | Lisa | NULL | NULL | Hello
9 | Lisa | 9 | NULL | NULL
I'd like to condense the rows down to the bare minimum with.
I want the following result:
Nr. | Name | Value 1 | Value 2 | Value 3
-----+------------+---------+---------+---------
1 | Max | 123 | 456 | 789
9 | Lisa | 1 | NULL | Hello
9 | Lisa | 3 | NULL | NULL
9 | Lisa | 9 | NULL | NULL
For condensing the rows with Max (Nr. 1) a group by of the max values would help.
select nr, name, max(val_1), max(val_2), max(val_3)
from table
group by nr, name
But I am unsure how to get the desired results for Lisa (Nr. 9). The row for Lisa contains a value in the Value 3 column, in this example it's condensed with the first row that matches Nr and Name and has a Null value in Value 3.
I'm thankful for every input!
Basic principle is same as Vladimir's solution. This uses UNPIVOT and PIVOT
with cte as
(
select nr, name, col, val,
rn = row_number() over(partition by nr, name, col order by val)
from [table]
unpivot
(
val
for col in (val_1, val_2, val_3)
) u
)
select *
from (
select nr, name, rn, col, val
from cte
) d
pivot
(
max (val)
for col in ([val_1], [val_2], [val_3])
) p
Here is one way to do it. Assign a unique row number for each column by sorting them in such a way that NULLs come last and then join them back together using these row numbers and remove rows with all NULLs.
Run just the CTE first and examine the intermediate result to understand how it works.
Sample data
DECLARE #T TABLE (Nr varchar(10), Name varchar(10), V1 varchar(10), V2 varchar(10), V3 varchar(10));
INSERT INTO #T VALUES
('1', 'Max ', '123' , NULL , NULL ),
('1', 'Max ', NULL , '456', NULL ),
('1', 'Max ', NULL , NULL , '789'),
('9', 'Lisa', '1' , NULL , NULL ),
('9', 'Lisa', '3' , NULL , NULL ),
('9', 'Lisa', NULL , NULL , 'Hello'),
('9', 'Lisa', '9' , NULL , NULL );
Query
WITH CTE
AS
(
SELECT
Nr
,Name
,V1
,V2
,V3
-- here we use CASE WHEN V1 IS NULL THEN 1 ELSE 0 END to put NULLs last
,ROW_NUMBER() OVER (PARTITION BY Nr ORDER BY CASE WHEN V1 IS NULL THEN 1 ELSE 0 END, V1) AS rn1
,ROW_NUMBER() OVER (PARTITION BY Nr ORDER BY CASE WHEN V2 IS NULL THEN 1 ELSE 0 END, V2) AS rn2
,ROW_NUMBER() OVER (PARTITION BY Nr ORDER BY CASE WHEN V3 IS NULL THEN 1 ELSE 0 END, V3) AS rn3
FROM #T AS T
)
SELECT
T1.Nr
,T1.Name
,T1.V1
,T2.V2
,T3.V3
FROM
CTE AS T1
INNER JOIN CTE AS T2 ON T2.Nr = T1.Nr AND T2.rn2 = T1.rn1
INNER JOIN CTE AS T3 ON T3.Nr = T1.Nr AND T3.rn3 = T1.rn1
WHERE
T1.V1 IS NOT NULL
OR T2.V2 IS NOT NULL
OR T3.V3 IS NOT NULL
ORDER BY
T1.Nr, T1.rn1
;
Result
+----+------+-----+------+-------+
| Nr | Name | V1 | V2 | V3 |
+----+------+-----+------+-------+
| 1 | Max | 123 | 456 | 789 |
| 9 | Lisa | 1 | NULL | Hello |
| 9 | Lisa | 3 | NULL | NULL |
| 9 | Lisa | 9 | NULL | NULL |
+----+------+-----+------+-------+

SQL Sum and Group by changing value of group

How do I group values ​​and change one of them when there is more than one
Table
ID | VALUE | NAME
1 | 2 | John
1 | 5 | Carl
2 | 4 | Elis
2 | 1 | Ted
3 | 2 | James
RESULT
ID | VALUE | NAME
1 | 7 | *
2 | 5 | *
3 | 2 | James
Here is one way that should work in any database:
select id, sum(value) as value,
(case when min(name) = max(name) then min(name) else '*' end) as name
from t
group by id;
This is the others query you must try
Select ID, sum(Value) Value, NAME = ( Select b.Name + ' ' AS [text()]
From dbo.test b
Where a.ID = b.ID
ORDER BY a.ID
For XML PATH (''))
from test a
group by ID
order by ID

Count Number of Consecutive Occurrence of values in Table

I have below table
create table #t (Id int, Name char)
insert into #t values
(1, 'A'),
(2, 'A'),
(3, 'B'),
(4, 'B'),
(5, 'B'),
(6, 'B'),
(7, 'C'),
(8, 'B'),
(9, 'B')
I want to count consecutive values in name column
+------+------------+
| Name | Repetition |
+------+------------+
| A | 2 |
| B | 4 |
| C | 1 |
| B | 2 |
+------+------------+
The best thing I tried is:
select Name
, COUNT(*) over (partition by Name order by Id) AS Repetition
from #t
order by Id
but it doesn't give me expected result
One approach is the difference of row numbers:
select name, count(*)
from (select t.*,
(row_number() over (order by id) -
row_number() over (partition by name order by id)
) as grp
from t
) t
group by grp, name;
The logic is easiest to understand if you run the subquery and look at the values of each row number separately and then look at the difference.
You could use windowed functions like LAG and running total:
WITH cte AS (
SELECT Id, Name, grp = SUM(CASE WHEN Name = prev THEN 0 ELSE 1 END) OVER(ORDER BY id)
FROM (SELECT *, prev = LAG(Name) OVER(ORDER BY id) FROM t) s
)
SELECT name, cnt = COUNT(*)
FROM cte
GROUP BY grp,name
ORDER BY grp;
db<>fiddle demo
The first cte returns group number:
+-----+-------+-----+
| Id | Name | grp |
+-----+-------+-----+
| 1 | A | 1 |
| 2 | A | 1 |
| 3 | B | 2 |
| 4 | B | 2 |
| 5 | B | 2 |
| 6 | B | 2 |
| 7 | C | 3 |
| 8 | B | 4 |
| 9 | B | 4 |
+-----+-------+-----+
And main query groups it based on grp column calculated earlier:
+-------+-----+
| name | cnt |
+-------+-----+
| A | 2 |
| B | 4 |
| C | 1 |
| B | 2 |
+-------+-----+
I have use Recursive CTE and minimise the use of row_number,also avoid count(*).
I think it will perform better,but in real world it depend what else filter you put to minimise number of rows affected.
If ID is having discreet values then One extra CTE will be use to generate continuous id.
;With CTE2 as
(
select ROW_NUMBER()over(order by id) id, name,1 Repetition ,1 Marker from #t
)
, CTE as
(
select top 1 cast(id as int) id, name,1 Repetition ,1 Marker from CTE2 order by id
union all
select a.id, a.name
, case when a.name=c.name then Repetition +1 else 1 end
, case when a.name=c.name then c.Marker else Marker+1 end
from #t a
inner join CTE c on a.id=c.id+1
)
,CTE1 as
(select *,ROW_NUMBER()over(partition by marker order by id desc)rn from cte c
)
select Name,Repetition from cte1 where rn=1

Summarise null rows in Oracle

I have a dataset like this:
+---------------+-------+
| SAMPLE_NUMBER | SCORE |
+---------------+-------+
| 1 | 100 |
| 2 | 97 |
| 3 | 124 |
| 4 | 762 |
| 5 | 999 |
| 6 | 1200 |
| 7 | NULL |
| 8 | NULL |
| 9 | NULL |
| 10 | NULL |
+---------------+-------+
I want to be able to summarise the NULL rows instead of displaying them all. So ideally, I would want the above to look like this:
+---------------+-------+
| SAMPLE_NUMBER | SCORE |
+---------------+-------+
| 1 | 100 |
| 2 | 97 |
| 3 | 124 |
| 4 | 762 |
| 5 | 999 |
| 6 | 1200 |
| 7-10 | NULL |
+---------------+-------+
Is there any way in Oracle to do this? Or is it something I will have to do post-query?
Yes. For your sample data:
select (case when score is null then min(sample_number) || '-' || max(sample_number)
else min(sample_number)
end) as sample_number,
score
from table t
group by score
order by min(id)
In other words, group by score and then fiddle with the sample number. Note: this assumes that you do not have duplicate scores. If you do, you can do so with a more complicated version:
select (case when score is null then min(sample_number) || '-' || max(sample_number)
else min(sample_number)
end) as sample_number,
score
from (select t.*,
row_number() over (partition by score order by sample_number) as seqnum
from table t
) t
group by score, (case when score is not null then seqnum end);
My guess is that this should be part of your presentation layer since you will have to cast sample_number to a string (assuming it is a numeric type. An alternative to your requirements is to return the min and max consecutive sample_number:
with t (SAMPLE_NUMBER, SCORE) as (
values (1, 100)
, (2, 97)
, (3, 124)
, (4, 762)
, (5, 999)
, (6, 1200)
, (7, NULL)
, (8, NULL)
, (9, NULL)
, (10, NULL)
)
select min(sample_number), max(sample_number), grp, score
from (
select SAMPLE_NUMBER, SCORE
, row_number() over (order by SAMPLE_NUMBER)
- row_number() over (partition by SCORE
order by SAMPLE_NUMBER) as grp
from t
) group by grp, score
order by grp;
1 2 GRP SCORE
----------- ----------- -------------------- -----------
1 1 0 100
2 2 1 97
3 3 2 124
4 4 3 762
5 5 4 999
6 6 5 1200
7 10 6 -
Tried against db2, so you may have to adjust it slightly.
Edit: treat rows as individuals when score is not null
with t (SAMPLE_NUMBER, SCORE) as (
values (1, 100)
, (2, 97)
, (3, 97)
, (4, 762)
, (5, 999)
, (6, 1200)
, (7, NULL)
, (8, NULL)
, (9, NULL)
, (10, NULL)
)
select min(sample_number), max(sample_number), grp, score
from (
select SAMPLE_NUMBER, SCORE
, row_number() over (order by SAMPLE_NUMBER)
- row_number() over (partition by SCORE
order by SAMPLE_NUMBER) as grp
from t
) group by grp, score
, case when score is not null then sample_number end
order by grp;
1 2 GRP SCORE
----------- ----------- -------------------- -----------
1 1 0 100
2 2 1 97
3 3 1 97
4 4 3 762
5 5 4 999
6 6 5 1200
7 10 6 -
You may want to map max to null in case it is the same as min:
[...]
select min(sample_number)
, nullif(max(sample_number), min(sample_number))
, grp
, score
from ...
1 2 GRP SCORE
----------- ----------- -------------------- -----------
1 - 0 100
2 - 1 97
3 - 1 97
4 - 3 762
5 - 4 999
6 - 5 1200
7 10 6 -
SELECT DISTINCT
DECODE(SCORE
,NULL
,(SELECT COUNT()+1
FROM TAB_NAME
WHERE SCORE IS NOT NULL)
|| '-'
|| (SELECT COUNT()
FROM TAB_NAME)
,SAMPLE_NUMBER) NUM
, NVL(TO_CHAR(SCORE),'NULL') SCRE
FROM TAB_NAME
ORDER BY 1 ASC;

Querying data groups with total row before starting next group?

I need to query some data in the below format in SQL Server:
Id Group Price
1 A 10
2 A 20
Sum 30
1 B 6
2 B 4
Sum 10
1 C 100
2 C 200
Sum 300
I was thinking to do it in the follwoing steps:
Query one group
In other query do sum
Use Union operator to combine this result set
Do step 1-3 for all groups and finally return all sub sets of data using union.
Is there a better way to do this ? May be using some out of box feature ? Please advise.
Edit:
As per suggestions and code sample I tried this code:
Select
Case
when id is null then 'SUM'
else CAST(id as Varchar(10)) end as ID,
Case when [group] is null then 'ALL' else CAST([group] as Varchar(50)) end as [group]
,Price from
(
SELECT Id, [Group],BGAApplicationID,Section, SUM(PrimaryTotalArea) AS price
FROM vwFacilityDetails
where bgaapplicationid=1102
GROUP BY Id, [Group],BGAApplicationID,Section WITH ROLLUP
) a
And Even this code as well:
Select Id, [Group],BGAApplicationID,Section, SUM(PrimaryTotalArea) AS price
From vwFacilityDetails
Where Not ([group] Is Null And id Is Null And BGAApplicationId is null and section is null) and BGAApplicationId=1102
Group By Id, [Group],BGAApplicationID,Section
With Rollup
In results it groups up the data but for every record it shows it 3 times (in both above codes) like:
2879 Existing Facilities Whole School 25.00
2879 Existing Facilities Whole School 25.00
2879 Existing Facilities Whole School 25.00
2879 ALL 25.00
I guess there is some issue in my query, please guide me here as well.
Thanks
SQL Server introduced GROUPING SETS which is what you should be looking to use.
SQL Fiddle
MS SQL Server 2008 Schema Setup:
Create Table vwFacilityDetails (
id int not null,
[group] char(1) not null,
PrimaryTotalArea int not null,
Section int,
bgaapplicationid int
);
Insert Into vwFacilityDetails (id, [group], Section,bgaapplicationid,PrimaryTotalArea) values
(1, 'A', 1,1102,2),
(1, 'A', 1,1102,1),
(1, 'A', 1,1102,7),
(2, 'A', 1,1102,20),
(1, 'B', 1,1102,6),
(2, 'B', 1,1102,4),
(1, 'C', 1,1102,100),
(2, 'C', 1,1102,200);
Query 1:
SELECT CASE WHEN Id is null then 'SUM'
ELSE Right(Id,10) end Id,
[Group],BGAApplicationID,Section,
SUM(PrimaryTotalArea) price
FROM vwFacilityDetails
where bgaapplicationid=1102
GROUP BY GROUPING SETS (
(Id,[Group],BGAApplicationID,Section),
([Group])
)
ORDER BY [GROUP],
ID;
Results:
| ID | GROUP | BGAAPPLICATIONID | SECTION | PRICE |
----------------------------------------------------
| 1 | A | 1102 | 1 | 10 |
| 2 | A | 1102 | 1 | 20 |
| SUM | A | (null) | (null) | 30 |
| 1 | B | 1102 | 1 | 6 |
| 2 | B | 1102 | 1 | 4 |
| SUM | B | (null) | (null) | 10 |
| 1 | C | 1102 | 1 | 100 |
| 2 | C | 1102 | 1 | 200 |
| SUM | C | (null) | (null) | 300 |
Select
id,
[Group],
SUM(price) AS price
From
Test
Group By
[group],
id
With
Rollup
http://sqlfiddle.com/#!3/080cd/8
Select Case when id is null then 'SUM' else CAST(id as Varchar(10)) end as ID
,Case when [group] is null then 'ALL' else CAST([group] as Varchar(10)) end as [group]
,Price from
(
SELECT id, [group], SUM(price) AS Price
FROM IG
GROUP BY [GROUP],ID WITH ROLLUP
) a