I have a table where I want to catch duplicate values.
After catching them I want to separate those results into separate rows.
The table:
CREATE TABLE [dbo].[ContactTable](
[contactid] [int] IDENTITY(1,1) NOT NULL,
[telephone1] [varchar](100) NULL,
[telephone2] [varchar](100) NULL,
[fullname] [varchar](100) NOT NULL
) ON [PRIMARY]
A few values:
insert into ContactTable(telephone1, telephone2,fullname) values('123','1234','danny')
insert into ContactTable(telephone1, telephone2,fullname) values('123','1234','danny1')
insert into ContactTable(telephone1, telephone2,fullname) values('123*','1234#','martin')
insert into ContactTable(telephone1, telephone2,fullname) values('1243*','15234#','martin')
insert into ContactTable(telephone1, telephone2,fullname) values('1243','15234','martin1')
The query for catching the duplicate values:
SELECT Phones, COUNT(Phones) AS CountPhones
FROM
(
SELECT Phones, contactid
FROM
(
SELECT telephone1 AS Phones, contactid
FROM ContactTable
WHERE (telephone1 IS NOT NULL)
UNION ALL
SELECT REPLACE(telephone1, '*', '') AS Phones, contactid
FROM ContactTable
WHERE (telephone1 IS NOT NULL) AND (telephone1 LIKE '%*')
UNION ALL
SELECT REPLACE(telephone1, '#', '') AS Phones, contactid
FROM ContactTable
WHERE (telephone1 IS NOT NULL) AND (telephone1 LIKE '%#')
UNION ALL
SELECT telephone2 AS Phones, contactid
FROM ContactTable
WHERE (telephone2 IS NOT NULL)
UNION ALL
SELECT REPLACE(telephone2, '*', '') AS Phones, contactid
FROM ContactTable
WHERE (telephone2 IS NOT NULL) AND (telephone2 LIKE '%*')
UNION ALL
SELECT REPLACE(telephone2, '#', '') AS Phones, contactid
FROM ContactTable
WHERE (telephone2 IS NOT NULL) AND (telephone2 LIKE '%#')
)as Tel
GROUP BY Phones, contactid
) as T
GROUP BY Phones
HAVING (COUNT(*) > 1)
ORDER BY CountPhones DESC
The results:
telephone Count
--------- -----
123 3
1234 3
1243 2
15234 2
But I want to get the results like this:
id telephone fullname
-- --------- --------
1 123 danny
2 123 danny1
3 123 martin
3 1234 martin
1 1234 danny
2 1234 danny1
5 1243 martin1
4 1243 martin
4 15234 martin
5 15234 martin1
How can I to do it?
SELECT * FROM (
SELECT
contactid AS id,
REPLACE(REPLACE(telephone1, '*', ''), '#', '') AS telephone,
fullname
FROM
ContactTable
UNION ALL
SELECT
contactid AS id,
REPLACE(REPLACE(telephone2, '*', ''), '#', '') AS telephone,
fullname
FROM ContactTable) AS all_phones
WHERE all_phones.telephone in (
SELECT phone FROM (
SELECT
COUNT(*) AS pnumber,
phone
FROM (
SELECT
REPLACE(REPLACE(telephone1, '*', ''), '#', '') AS phone
FROM
ContactTable
UNION ALL
SELECT
REPLACE(REPLACE(telephone2, '*', ''), '#', '') AS phone
FROM
ContactTable ) AS phones
GROUP BY phone)
AS phone_numbers WHERE pnumber > 1)
Related
I have a table named Employee_audit with following schema,
emp_audit_id
eid
name
salary
...
1
1
Daniel
1000
...
2
1
Dani
1000
...
3
1
Danny
3000
...
My goal is to write a SQL query which will return in following format, considering the first row also as changed value from null.
columnName
oldValue
newValue
name
null
Daniel
salary
null
1000
name
Daniel
Dani
name
Dani
Danny
salary
1000
3000
...
...
...
Finally reached to below solution
CREATE TABLE Employee_audit (
emp_audit_id int,
eid int,
name varchar(50),
salary int,
department varchar(50)
)
insert into Employee_audit (emp_audit_id, eid, name, salary,department)
values
(1, 1, 'Daniel', 1000,'ROP'),
(2, 1, 'Dani', 1000,'ROP'),
(3, 1, 'Danny', 3000,'ROP');
with diffs as (
select 'name' colName, emp_audit_id, eid, lag(name, 1, null) over (partition by eid order by emp_audit_id) oldValue, name newValue
from Employee_audit
union all
select 'salary', emp_audit_id, eid, cast(lag(salary, 1, null) over (partition by eid order by emp_audit_id) as varchar), cast(salary as varchar) newValue
from Employee_audit
union all
...
)
select *
from diffs
where oldValue <> newValue or oldValue is null
order by emp_audit_id, eid
Returns:
emp_audit_id
columnName
oldValue
newValue
1
name
NULL
Daniel
1
salary
NULL
1000.00
2
name
Daniel
Dani
3
name
Dani
Danny
3
salary
1000.00
3000.00
...
...
...
...
But the problem is, the query is very slow because to track 10 fields we have to write 10 union all.
How can I more optimize the query like in a single scan how can I do it?
Here is an option that will dynamically unpivot your data without actually using Dynamic SQL.
Example
;with cte as (
Select emp_audit_id
,eid
,[key]
,newValue=[value]
,oldvalue = lag(value) over (partition by eid,[key] order by emp_audit_id)
From Employee_audit A
Cross Apply ( Select [key],[value] From OpenJson( (Select A.* For JSON Path,Without_Array_Wrapper ) ) ) B
Where [key] not in ('emp_audit_id','eid')
)
Select emp_audit_id
,columName = [key]
,oldvalue
,newvalue
From cte
Where newvalue<>oldvalue or oldvalue is null
Returns
emp_audit_id columName oldvalue newvalue
1 department NULL ROP
1 name NULL Daniel
2 name Daniel Dani
3 name Dani Danny
1 salary NULL 1000
3 salary 1000 3000
I would use apply:
select t.emp_audit_id, v.columnName, v.newValue,
lag(v.newValue) over (partition by eid, columnName order by emp_audit_id) as oldValue
from some_table t cross apply
(values ('name', t.name),
('salary', t.salary),
. . .
) v (columnName, newValue);
If you need to cast the values so they are strings, that goes in the values clause:
select t.emp_audit_id, v.columnName, v.newValue,
lag(v.newValue) over (partition by eid, ColumnName order by emp_audit_id) as oldValue
from some_table t cross apply
(values ('name', t.name),
('salary', cast(t.salary as varchar(255))),
. . .
) v (columnName, newValue);
I have a table
name | age | city
-------------
joe | 42 | berlin
ben | 42 | munich
anna | 22 | hamburg
pia | 50 | berlin
georg | 42 | munich
lisa | 42 | berlin
Now I would like to get all 42 year old in different columns by city
berlin | munich
-------------
joe | ben
lisa | georg
So I would need something like
SELECT (
SELECT name AS berlin WHERE city = "berlin"
UNION
SELECT name AS munich WHERE city = "munich")
FROM TABLE
WHERE
age = 42
Best from Berlin
Joerg
I think you want aggregation:
select max(case when city = 'berlin' then name end) as berlin,
max(case when city = 'munich' then name end) as munich
from (select t.*, row_number() over (partition by city order by name) as seqnum
from t
where city in ('berlin', 'munich') and age = 42
) t
group by seqnum
order by seqnum;
Using PIVOT for example I did it with SQL Server. Hope this can help you.
DECLARE #columns varchar(MAX);
DECLARE #sql nvarchar(max)
CREATE TABLE #Table
(
idPerson int,
firstName varchar(10),
age int,
city varchar(10)
);
INSERT INTO #Table
SELECT '1', 'joe', '42','berlin' UNION ALL
SELECT '2', 'ben', '42','munich' UNION ALL
SELECT '3', 'Ana', '22','hamburg' UNION ALL
SELECT '4', 'pia', '50','berlin' UNION ALL
SELECT '5', 'george', '42','munich' UNION ALL
SELECT '6', 'lisa', '42','munich'
--SELECT * from #Table
SET #columns = STUFF(
(
SELECT
',' + QUOTENAME(LTRIM(city))
FROM
(SELECT DISTINCT city
FROM #Table
) AS T
ORDER BY
city
FOR XML PATH('')
), 1, 1, '');
SET #sql = N'
SELECT
*
FROM
(
SELECT idPerson ,firstName ,age ,city
FROM #Table
--WHERE age = 42
) AS T
PIVOT
(
MAX(firstName)
FOR city IN (' + #columns + N')
) AS P order by age;';
EXEC sp_executesql #sql;
DROP TABLE #Table;
Thanks for your help! Based on the answer of Gordon Linhoff I got this solution:
SELECT max(case when city = 'berlin' then name end) as berlin,
max(case when city = 'munich' then name end) as munich
FROM TABLE
WHERE age = 42
;
I'm looking for a way to get table below:
CREATE TABLE [dbo].[#temp]
(
[ID_TASK] [NVARCHAR](300) NULL,
[CHNAME_NAME] [NVARCHAR](300) NULL,
[CHNAME_PHONE] [NVARCHAR](300) NULL
) ON [PRIMARY]
INSERT INTO [dbo].[#temp]
VALUES ('ID005', 'Anderson Abreu Oliveira', '68157120'),
('ID006', 'Gonzalez-IV', '64106929'),
('ID009', 'Parker W.H.', '60994308')
I'm using this SQL query:
SELECT
ID_TASK, CHNAME_NAME,
RTRIM(LTRIM(SUBSTRING(CHNAME_NAME, 1, CHARINDEX(' ', CHNAME_NAME)))) AS SURNAME,
RTRIM(LTRIM(SUBSTRING(CHNAME_NAME, CHARINDEX(' ', CHNAME_NAME) + 1, LEN(CHNAME_NAME) - (CHARINDEX(' ', CHNAME_NAME) - 1)))) AS FIRSTNAME,
REPLACE((RTRIM(LTRIM(CHNAME_PHONE))), '8-', '') AS CHNAME_PHONE
FROM
[dbo].[#temp]
but I get this result:
How do I fix this SQL query to get the desired result? Thank you
You might try to use case..when structure
with CHARINDEX('-', CHNAME_NAME) and CHARINDEX(' ', CHNAME_NAME)
as in the following statement :
SELECT
ID_TASK, CHNAME_NAME,
RTRIM(LTRIM(SUBSTRING(CHNAME_NAME, 1,
(Case CHARINDEX(' ', CHNAME_NAME) When 0 Then CHARINDEX('-', CHNAME_NAME)-1
Else CHARINDEX(' ', CHNAME_NAME) End)))) AS SURNAME,
RTRIM(LTRIM(SUBSTRING(CHNAME_NAME,
(Case CHARINDEX(' ', CHNAME_NAME) When 0 Then CHARINDEX('-', CHNAME_NAME)
Else CHARINDEX(' ', CHNAME_NAME) End) + 1,
LEN(CHNAME_NAME) - (CHARINDEX(' ', CHNAME_NAME) - 1)))) AS FIRSTNAME,
REPLACE((RTRIM(LTRIM(CHNAME_PHONE))), '8-', '') AS CHNAME_PHONE
FROM
[dbo].[#temp]
This is quite an advanced thing to achieve I've done some SQL to get you there, but not tidied it up...
DECLARE #temp TABLE (
[ID_TASK] [nvarchar](300) NULL,
[CHNAME_NAME] [nvarchar](300) NULL,
[CHNAME_PHONE] [nvarchar](300) NULL
)
INSERT INTO #temp
SELECT 'ID005','Anderson Abreu Oliveira','68157120' UNION ALL
SELECT 'ID006','Gonzalez-IV','64106929' UNION ALL
SELECT 'ID009','Parker W.H.','60994308'
select ID_TASK, col1, col2, col3
from
(
select a.ID_TASK, Item , 'col' + CONVERT(NVARCHAR(10), ItemNumber) ItemNumber
from #temp a
CROSS APPLY [dbo].[DelimitedSplit] ( REPLACE(a.CHNAME_NAME, '-', ' '), ' ' ) b
) d
pivot
(
MAX(item)
for itemNumber in (col1, col2, col3)
) piv
GROUP BY ID_TASK, col1, col2, col3
Helper function:
CREATE FUNCTION [dbo].[DelimitedSplit] (
#pString VARCHAR(MAX),
#pDelimiter CHAR(1)
)
RETURNS TABLE WITH SCHEMABINDING
AS
RETURN
WITH E1(N) AS ( -- 10
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, E1 b),
E3(N) AS (SELECT 1 FROM E2 a, E2 b, E2 c),
E4(N) AS (SELECT 1 FROM E3 a, E3 b, E3 c, E3 d),
cteTally(N) AS (
SELECT 0 UNION ALL
SELECT TOP ( DATALENGTH( ISNULL( #pString, 1 ) ) ) ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL ) ) FROM E4
),
cteStart(N1) AS (
SELECT t.N + 1
FROM cteTally t
WHERE ( SUBSTRING( #pString, t.N, 1 ) = #pDelimiter OR t.N = 0 )
)
-- ------
SELECT
ItemNumber = ROW_NUMBER() OVER( ORDER BY s.N1 ),
Item = SUBSTRING( #pString, s.N1, ISNULL( NULLIF( CHARINDEX( #pDelimiter, #pString, s.N1 ), 0) - s.N1, 1000000 ) )
FROM cteStart s
GO
Here you go:
CREATE TABLE [dbo].[#temp]
(
[ID_TASK] [NVARCHAR](300) NULL,
[CHNAME_NAME] [NVARCHAR](300) NULL,
[CHNAME_PHONE] [NVARCHAR](300) NULL
) ON [PRIMARY]
INSERT INTO [dbo].[#temp]
VALUES ('ID005', 'Anderson Abreu Oliveira', '68157120'),
('ID006', 'Gonzalez-IV', '64106929'),
('ID009', 'Parker W.H.', '60994308');
WITH CTE AS
(
SELECT [ID_TASK],
LEFT([CHNAME_NAME], IIF( CHARINDEX(' ', [CHNAME_NAME]) = 0 , CHARINDEX('-', [CHNAME_NAME])-1, CHARINDEX(' ', [CHNAME_NAME]))) AS T,
[CHNAME_NAME],
[CHNAME_PHONE]
FROM #Temp
)
SELECT [ID_TASK],
[CHNAME_NAME],
T AS SURNAME,
LTRIM(REPLACE(CASE WHEN (T IS NULL) OR (T = '') THEN [CHNAME_NAME] ELSE SUBSTRING([CHNAME_NAME], LEN(T)+1, LEN([CHNAME_NAME])) END, '-', '')) FirstName,
[CHNAME_PHONE]
FROM CTE;
Results:
+---+---------+-------------------------+-----------+-----------------+--------------+
| | ID_TASK | CHNAME_NAME | SURNAME | FirstName | CHNAME_PHONE |
+---+---------+-------------------------+-----------+-----------------+--------------+
| 1 | ID005 | Anderson Abreu Oliveira | Anderson | Abreu Oliveira | 68157120 |
| 2 | ID006 | Gonzalez-IV | Gonzalez | IV | 64106929 |
| 3 | ID009 | Parker W.H. | Parker | W.H. | 60994308 |
+---+---------+-------------------------+-----------+-----------------+--------------+
Demo
I have the following table:
id Prefix FisrtName LastName
--------------------------------------------
123 Mr Lynn Berg
123 Ms Madeline Owen
123 Mrs Zelenia Sellers
101 Mrs Jesse Vincent
101 Mr Chaim Long
The result table should look like this
id name1 name2 name2
-----------------------------------------------------------
123 Mr Lynn Berg Ms MadelineOwen Mrs Zelenia Sellers
101 Mrs Jesse Vincent Mr Chaim Long
How could I achieve this result in SQL Server? Can I use pivot function?
Please help
You can also do conditional aggregation :
select id, max(case when seq = 1 then Name end) as Name1,
max(case when seq = 2 then Name end) as Name2,
max(case when seq = 3 then Name end) as Name3
from (select id, concat(Prefix,' ',FisrtName,' ',LastName) as Name,
row_number() over (partition by id order by (select null)) as seq
from table
) t
group by id;
Try this simple pivot:
declare #tbl table (id int, Prefix varchar(3), FirstName varchar(20), LastName varchar(20));
insert into #tbl values
(123, 'Mr', 'Lynn', 'Berg'),
(123, 'Ms', 'Madeline', 'Owen'),
(123, 'Mrs', 'Zelenia', 'Sellers'),
(101, 'Mrs', 'Jesse', 'Vincent'),
(101, 'Mr', 'Chaim', 'Long');
select id, [1] [Name1], [2] [Name2], [3] [Name3] from (
select id,
ROW_NUMBER() over (partition by id order by (select null)) rn,
Prefix + ' ' + FirstName + ' ' + LastName [FullName]
from #tbl
) a pivot (
max(fullname) for rn in ([1],[2],[3])
) b;
CREATE TABLE #Table1 (
id INT
,Prefix VARCHAR(3)
,FisrtName VARCHAR(8)
,LastName VARCHAR(7)
);
INSERT INTO #Table1
VALUES (
123
,'Mr'
,'Lynn'
,'Berg'
)
,(
123
,'Ms'
,'Madeline'
,'Owen'
)
,(
123
,'Mrs'
,'Zelenia'
,'Sellers'
)
,(
101
,'Mrs'
,'Jesse'
,'Vincent'
)
,(
101
,'Mr'
,'Chaim'
,'Long'
)
SELECT *
FROM #Table1
SELECT id
,[1] [Name1]
,[2] [Name2]
,[3] [Name3]
FROM (
SELECT id
,CONCAT (
PREFIX
,FISRTNAME
,LASTNAME
) AS Namm
,ROW_NUMBER() OVER (
PARTITION BY id ORDER BY (
id
)
) AS rn
FROM #TABLE1
) a
pivot(max(Namm) FOR rn IN (
[1]
,[2]
,[3]
)) b
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