Is there a chance to show only one row if there are any rows with same values?
I've the following scenario:
ID | Column A | Column B | Column C
1 | 2 | 'test' | 5
2 | 3 | 'test'| 6
3 | 2 | 'test'| 5
In this scenario I want only show the following resultset:
ID | Column A | Column B | Column C
1 | 2 | 'test' | 5
2 | 3 | 'test'| 6
Thanks for your help.
Regards, pro
Your rows are not exact duplicates, because of the id column. If you don't care which value of the id appears, you can do what you want as:
select max(id) as id, ColumnA, ColumnB, ColumnC
from t
group by ColumnA, ColumnB, ColumnC
If you don't need the id at all, this is simpler:
select distinct ColumnA, ColumnB, ColumnC
from t
Try this :
With cte As
( Select * , row_number() over (partition by ColumnA, ColumnB,ColumnC
order by ID ) as myrownumber from myTable
)
Select * from cte where myrownumber=1
select id, column1,column2,colum3 from
(
select *, row_number() over (partition by column1,column2,colum3 order by id) as sno
from table
) as t
where sno=1
CREATE TABLE #test
(
ID TINYINT NOT NULL,
colA TINYINT NOT NULL,
colB VARCHAR(10) NOT NULL,
colC TINYINT NOT NULL
);
INSERT INTO #test VALUES (1,2, 'test', 5);
INSERT INTO #test VALUES (2,3, 'test', 6);
INSERT INTO #test VALUES (3,2, 'test', 5);
SELECT
ID,
ColA,
ColB,
ColC
FROM
(
SELECT
ID,
ColA,
ColB,
ColC,
ROW_NUMBER() OVER(PARTITION BY ColA ORDER BY ColA DESC) AS RowNum
FROM #test
) AS WorkTable
WHERE RowNum = 1
Related
I'm trying to get the Col A records based on the first character on Col B. If the first character in Col B do matches with for all unique values of Col A, then the Col A value to be returned. If the first character in Col B does not match the other first values in Col B, then the col A value should not be returned. This is in Oracle SQL.
select T1.colA,count(*) from colTables T1 where T1.colA in (
select T2.colA from colTables T2 where T2.colB like '1%'
group by T2.colA)
group by T1.colA;
Col A | Col B
101|12541
101|15475
101|19874
102|12544
102|22549
102|12537
103|22549
103|28747
104|72549
104|82549
104|82549
105|12549
105|12531
105|12589
106|75448
106|71544
My query gives the following output
ColA | Count
101|3
102|3
105|3
but I want the output to be
ColA| Count
101|3
105|3
Also.. I am checking if there is any means where I can omit T2.colB like '1%' to get the output in the following way
ColA| Count
101|3 -- All values in col B starts with 1
103|2 -- All values in col B starts with 2
105|3 -- All values in col B starts with 1
106|2 -- All values in col B starts with 7
If you want all colB values to start with a 1, then:
select t.colA, count(*)
from colTables t
group by t.colA
having sum(case when t.colB like '1%' then 1 else 0 end) = count(*);
You could phrase this in other ways as well, such as:
having min(t.colB) >= '1' and
max(t.colB) < '2'
If you just want that the colB values start with the same letter for all colA, use:
having min(substr(t.colB, 1, 1)) = max(substr(t.colB, 1, 1))
Schema
CREATE TABLE tbl (
"cola" varchar(100),
"colb" varchar(100)
);
INSERT INTO tbl
("cola", "colb")
VALUES
('101', '12541'),
('101', '15475'),
('101', '19874'),
('102', '12544'),
('102', '22549'),
('102', '12537'),
('103', '22549'),
('103', '28747'),
('104', '72549'),
('104', '82549'),
('104', '82549'),
('105', '12549'),
('105', '12531'),
('105', '12589'),
('106', '75448'),
('106', '71544');
Query
Live test: https://www.db-fiddle.com/f/iquHToVTGz8JxnWSaT2ChD/4
select cola, count(*)
from tbl
group by cola
having (cola, count(*))
in (
select
cola, count(*)
from tbl
where colb like '1%'
group by cola
)
order by cola;
Output:
| cola | count |
| ---- | ----- |
| 105 | 3 |
| 101 | 3 |
Another approach, works on RDBMS that does not supports to tuple in IN clause. Use EXISTS:
select y.cola, count(*) as y_count
from tbl y
group by y.cola
having exists
(
select
null -- does not matter
from tbl x
-- this matters
where x.cola = y.cola
and x.colb like '1%'
group by x.cola
having count(x.cola) = count(y.cola)
)
order by y.cola;
What I have
From the following #MyTable I just have Name and Number columns.
My goal is to fill the valus where Number = NULL with a progressive number and get the values I have wrote into the Desidered_col column.
+------+--------+---------------+
| Name | Number | Desidered_col |
+------+--------+---------------+
| John | 1 | 1 |
| John | 2 | 2 |
| John | 3 | 3 |
| John | NULL | 4 |
| John | NULL | 5 |
| John | 6 | 6 |
| Mike | 1 | 1 |
| Mike | 2 | 2 |
| Mike | NULL | 3 |
| Mike | 4 | 4 |
| Mike | 5 | 5 |
| Mike | 6 | 6 |
+------+--------+---------------+
What I have tried
I have tried with the following query
SELECT Name, Number, row_number() OVER(PARTITION BY [Name] ORDER BY Number ASC) AS rn
FROM #MyTable
but it put all the NULL values first and then count the rows.
How can I fill the empty values?
Why I don't think is a duplicate question
I have read this question and this question but I don't think it is duplicate because they don't consider the PARTITION BY construct.
This is the script to create and populate the table
SELECT *
INTO #MyTable
FROM (
SELECT 'John' AS [Name], 1 AS [Number], 1 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], 2 AS [Number], 2 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], 3 AS [Number], 3 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], NULL AS [Number], 4 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], NULL AS [Number], 5 AS [Desidered_col] UNION ALL
SELECT 'John' AS [Name], 6 AS [Number], 6 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 1 AS [Number], 1 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 2 AS [Number], 2 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], NULL AS [Number], 3 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 4 AS [Number], 4 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 5 AS [Number], 5 AS [Desidered_col] UNION ALL
SELECT 'Mike' AS [Name], 6 AS [Number], 6 AS [Desidered_col]
) A
This query is a bit complicated but seems to return your expected result. The only case it may be wrong is when someone does not have Number = 1.
The idea is that you must find gaps between numbers and count how many null values can be used to fill them.
Sample data
create table #myTable (
[Name] varchar(20)
, [Number] int
)
insert into #myTable
insert into #myTable
SELECT 'John' AS [Name], 1 AS [Number] UNION ALL
SELECT 'John' AS [Name], 2 AS [Number]UNION ALL
SELECT 'John' AS [Name], 3 AS [Number] UNION ALL
SELECT 'John' AS [Name], NULL AS [Number] UNION ALL
SELECT 'John' AS [Name], NULL AS [Number] UNION ALL
SELECT 'John' AS [Name], 6 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 1 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 2 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], NULL AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 4 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 5 AS [Number] UNION ALL
SELECT 'Mike' AS [Name], 6 AS [Number]
Query
;with gaps_between_numbers as (
select
t.Name, cnt = t.nextNum - t.Number - 1, dr = dense_rank() over (partition by t.Name order by t.Number)
, rn = row_number() over (partition by t.Name order by t.Number)
from (
select
Name, Number, nextNum = isnull(lead(Number) over (partition by Name order by number), Number + 1)
from
#myTable
where
Number is not null
) t
join master.dbo.spt_values v on t.nextNum - t.Number - 1 > v.number
where
t.nextNum - t.Number > 1
and v.type = 'P'
)
, ordering_nulls as (
select
t.Name, dr = isnull(q.dr, 2147483647)
from (
select
Name, rn = row_number() over (partition by Name order by (select 1))
from
#myTable
where
Number is null
) t
left join gaps_between_numbers q on t.Name = q.Name and t.rn = q.rn
)
, ordering_not_null_numbers as (
select
Name, Number, rn = dense_rank() over (partition by Name order by gr)
from (
select
Name, Number, gr = sum(lg) over (partition by Name order by Number)
from (
select
Name, Number, lg = iif(Number - lag(Number) over (partition by Name order by Number) = 1, 0, 1)
from
#myTable
where
Number is not null
) t
) t
)
select
Name, Number
, Desidered_col = row_number() over (partition by Name order by rn, isnull(Number, 2147483647))
from (
select * from ordering_not_null_numbers
union all
select Name, null, dr from ordering_nulls
) t
CTE gaps_between_numbers is seeking for numbers that are not consecutive. Number difference between current and next row shows how many NULL values can be used to fill the gaps. Then master.dbo.spt_values is used to multiply each row by that amount. In gaps_between_numbers dr column is gap number and cnt is amount of NULL values that need to used.
ordering_nulls orders only NULL values and is joined with CTE gaps_between_numbersto know in which position each row should appear.
ordering_not_null_numbers orders values that are not NULL. Consecutive Numbers will have same row number
And last step is to union CTE's ordering_not_null_numbers and ordering_nulls and make desired ordering
Rextester DEMO
In order to do this, you need a column that specifies the order of the rows in the table. You can do this using the identity() function:
SELECT identity(int, 1, 1) as MyTableId, a.*
INTO #MyTable
. . .
I'm pretty sure SQL Server will follow the ordering of a values() statement and in practice will follow the ordering of a union all. You can explicitly put this column in each row, if you prefer.
Then you can use this to assign your value:
select t.*,
row_number() over (partition by name order by mytableid) as desired_col
from #MyTable
You could also assign the new ranking based on Desidered_col using row_number() function with ORDER BY clause (select 1 or select null)
select *,
row_number() over (partition by Name order by (select 1)) New_Desidered_col
from #MyTable
I have a table with the columns below, and I need to get the values if COD is duplicated, get the non NULL on VALUE column. If is not duplicated, it can get a NULL VALUE. Like the example:
I'm using SQL SERVER.
This is what I get:
COD ID VALUE
28 1 NULL
28 2 Supermarket
29 1 NULL
29 2 School
29 3 NULL
30 1 NULL
This is what I want:
COD ID VALUE
28 2 Supermarket
29 2 School
30 1 NULL
What I'm tryin' to do:
;with A as (
(select DISTINCT COD,ID,VALUE from CodId where ID = 2)
UNION
(select DISTINCT COD,ID,NULL from CodId where ID != 2)
)select * from A order by COD
You can try this.
DECLARE #T TABLE (COD INT, ID INT, VALUE VARCHAR(20))
INSERT INTO #T
VALUES(28, 1, NULL),
(28, 2 ,'Supermarket'),
(29, 1 ,NULL),
(29, 2 ,'School'),
(29, 3 ,NULL),
(30, 1 ,NULL)
;WITH CTE AS (
SELECT *, RN= ROW_NUMBER() OVER (PARTITION BY COD ORDER BY VALUE DESC) FROM #T
)
SELECT COD, ID ,VALUE FROM CTE
WHERE RN = 1
Result:
COD ID VALUE
----------- ----------- --------------------
28 2 Supermarket
29 2 School
30 1 NULL
Another option is to use the WITH TIES clause in concert with Row_Number()
Example
Select top 1 with ties *
from YourTable
Order By Row_Number() over (Partition By [COD] order by Value Desc)
Returns
COD ID VALUE
28 2 Supermarket
29 2 School
30 1 NULL
I would use GROUP BY and JOIN. If there is no NOT NULL value for a COD than it should be resolved using the OR in JOIN clause.
SELECT your_table.*
FROM your_table
JOIN (
SELECT COD, MAX(value) value
FROM your_table
GROUP BY COD
) gt ON your_table.COD = gt.COD and (your_table.value = gt.value OR gt.value IS NULL)
If you may have more than one non null value for a COD this will work
drop table MyTable
CREATE TABLE MyTable
(
COD INT,
ID INT,
VALUE VARCHAR(20)
)
INSERT INTO MyTable
VALUES (28,1, NULL),
(28,2,'Supermarket'),
(28,3,'School'),
(29,1,NULL),
(29,2,'School'),
(29,3,NULL),
(30,1,NULL);
WITH Dups AS
(SELECT COD FROM MyTable GROUP BY COD HAVING count (*) > 1 )
SELECT MyTable.COD,MyTable.ID,MyTable.VALUE FROM MyTable
INNER JOIN dups ON MyTable.COD = Dups.COD
WHERE value IS NOT NULL
UNION
SELECT MyTable.COD,MyTable.ID,MyTable.VALUE FROM MyTable
LEFT JOIN dups ON MyTable.COD = Dups.COD
WHERE dups.cod IS NULL
Using T-SQL for this table:
+-----+------+------+------+-----+
| No. | Col1 | Col2 | Col3 | Age |
+-----+------+------+------+-----+
| 1 | e | a | o | 5 |
| 2 | f | b | a | 34 |
| 3 | a | NULL | b | 22 |
| 4 | b | c | a | 55 |
| 5 | b | a | b | 19 |
+-----+------+------+------+-----+
I need to count the TOP 3 names (Ordered by TotalCount DESC) across all rows and columns, for 3 Age groups: 0-17, 18-49, 50-100. Also, how do I ignore the NULLS from my results?
If it's possible, how I can also UNION the results for all 3 age groups into one output table to get 9 results (TOP 3 x 3 Age groups)?
Output for only 1 Age Group: 18-49 would look like this:
+------+------------+
| Name | TotalCount |
+------+------------+
| b | 4 |
| a | 3 |
| f | 1 |
+------+------------+
You need to unpivot first your table and then exclude the NULLs. Then do a simple COUNT(*):
WITH CteUnpivot(Name, Age) AS(
SELECT x.*
FROM tbl t
CROSS APPLY ( VALUES
(col1, Age),
(col2, Age),
(col3, Age)
) x(Name, Age)
WHERE x.Name IS NOT NULL
)
SELECT TOP 3
Name, COUNT(*) AS TotalCount
FROM CteUnpivot
WHERE Age BETWEEN 18 AND 49
GROUP BY Name
ORDER BY COUNT(*) DESC
ONLINE DEMO
If you want to get the TOP 3 for each age group:
WITH CteUnpivot(Name, Age) AS(
SELECT x.*
FROM tbl t
CROSS APPLY ( VALUES
(col1, Age),
(col2, Age),
(col3, Age)
) x(Name, Age)
WHERE x.Name IS NOT NULL
),
CteRn AS (
SELECT
AgeGroup =
CASE
WHEN Age BETWEEN 0 AND 17 THEN '0-17'
WHEN Age BETWEEN 18 AND 49 THEN '18-49'
WHEN Age BETWEEN 50 AND 100 THEN '50-100'
END,
Name,
COUNT(*) AS TotalCount
FROM CteUnpivot
GROUP BY
CASE
WHEN Age BETWEEN 0 AND 17 THEN '0-17'
WHEN Age BETWEEN 18 AND 49 THEN '18-49'
WHEN Age BETWEEN 50 AND 100 THEN '50-100'
END,
Name
)
SELECT
AgeGroup, Name, TotalCount
FROM(
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY AgeGroup, Name ORDER BY TotalCount DESC)
FROM CteRn
) t
WHERE rn <= 3;
ONLINE DEMO
The unpivot technique using CROSS APPLY and VALUES:
An Alternative (Better?) Method to UNPIVOT (SQL Spackle) by Dwain Camps
You can check below multiple-CTE SQL select statement
Row_Number() with Partition By clause is used ordering records within each group categorized by ages
/*
CREATE TABLE tblAges(
[No] Int,
Col1 VarChar(10),
Col2 VarChar(10),
Col3 VarChar(10),
Age SmallInt
)
INSERT INTO tblAges VALUES
(1, 'e', 'a', 'o', 5),
(2, 'f', 'b', 'a', 34),
(3, 'a', NULL, 'b', 22),
(4, 'b', 'c', 'a', 55),
(5, 'b', 'a', 'b', 19);
*/
;with cte as (
select
col1 as col, Age
from tblAges
union all
select
col2, Age
from tblAges
union all
select
col3, Age
from tblAges
), cte2 as (
select
col,
case
when age < 18 then '0-17'
when age < 50 then '18-49'
else '50-100'
end as grup
from cte
where col is not null
), cte3 as (
select
grup,
col,
count(grup) cnt
from cte2
group by
grup,
col
)
select * from (
select
grup, col, cnt, ROW_NUMBER() over (partition by grup order by cnt desc) cnt_grp
from cte3
) t
where cnt_grp <= 3
order by grup, cnt
I have a table of logs that contain a ID and TIMESTAMP. I want to ORDER BY ID and then TIMESTAMP.
For example, this is what the result set would look like:
12345 05:40
12345 05:50
12345 06:22
12345 07:55
12345 08:33
Once that's done, I want to INSERT a order value in a third column that signifies it's placement in the group from earliest to latest.
So, you would have something like this:
12345 05:40 1 <---First entry
12345 05:50 2
12345 06:22 3
12345 07:55 4
12345 08:33 5 <---Last entry
How can I do that in a SQL statement? I can select the data and ORDER BY ID, TIMESTAMP. But, I can't seem to INSERT a order value based on the groupings. :(
Try this update not an insert:
Fiddle demo here:
;with cte as(
select id, yourdate, row_number() over(order by id,yourdate) rn
from yourTable
)
Update ut Set thirdCol = rn
From yourTable ut join cte on ut.Id = cte.id and ut.yourdate = cte.yourdate
NOTE: if you need to get the thirdColumn updated per id basis, please partition your rownumber by using row_number() over (partition by id, order by order by id,yourdate)
Results:
| ID | YOURDATE | THIRDCOL |
|-------|----------|----------|
| 12345 | 05:40 | 1 |
| 12345 | 05:50 | 2 |
| 12345 | 06:22 | 3 |
| 12345 | 07:55 | 4 |
| 12345 | 08:33 | 5 |
Using a derived table and an update.
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
drop table #TableOne
end
CREATE TABLE #TableOne
(
SomeColumnA int ,
LetterOfAlphabet varchar(12) ,
PositionOrdinal int not null default 0
)
INSERT INTO #TableOne ( SomeColumnA , LetterOfAlphabet )
select 123 , 'x'
union all select 123 , 'b'
union all select 123 , 'z'
union all select 123 , 't'
union all select 123 , 'c'
union all select 123 , 'd'
union all select 123 , 'e'
union all select 123 , 'a'
Select 'pre' as SpaceTimeContinium , * from #TableOne order by LetterOfAlphabet
Update
#TableOne
Set PositionOrdinal = derived1.rowid
From
( select SomeColumnA , LetterOfAlphabet , rowid = row_number() over (order by LetterOfAlphabet asc) from #TableOne innerT1 )
as derived1
join #TableOne t1
on t1.LetterOfAlphabet = derived1.LetterOfAlphabet and t1.SomeColumnA = derived1.SomeColumnA
Select 'post' as SpaceTimeContinium, * from #TableOne order by LetterOfAlphabet
IF OBJECT_ID('tempdb..#TableOne') IS NOT NULL
begin
drop table #TableOne
end
To get the order you desire without doing an insert and an update, you can set your clustered index to handle it for you. The example below creates a clustered primary key.
To do this you must remove any clustered index that you already have on the table because you can only have one clustered index per table.
CREATE TABLE dbo.Table_1
(
ID int NOT NULL,
DTStamp datetime NOT NULL
)
ALTER TABLE dbo.Table_1 ADD CONSTRAINT
PK_Table_1 PRIMARY KEY CLUSTERED
(
ID,
DTStamp
)
Insert some random data to test with...
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12346,getdate());
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12346,dateadd(mi,1,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12346,dateadd(mi,2,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12346,dateadd(mi,3,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12346,dateadd(mi,4,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12340,dateadd(mi,5,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12340,dateadd(mi,6,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12340,dateadd(mi,7,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12340,dateadd(mi,8,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12344,dateadd(mi,1,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12344,dateadd(mi,2,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12344,dateadd(mi,3,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12344,dateadd(mi,4,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12344,dateadd(mi,5,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12344,dateadd(mi,6,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12344,dateadd(mi,7,getdate()));
INSERT INTO [dbo].[Table_1]([ID],[DTStamp])VALUES(12344,dateadd(mi,8,getdate()));
Now query your table and check out the order...
SELECT [ID] ,[DTStamp] FROM [Table_1]
If you need the order to display in a query, you can add the row number with an over clause.
SELECT [ID] ,[DTStamp],row_number() over (partition by [ID] order by [ID] ,[DTStamp]) as SortOdr FROM [Table_1]