Create Distinct Column Values as Extra Rows - sql

Any tricks anyone can share on how to manipulate the following table
ID TYPE Name Description
1 X A DESC_A
2 X B DESC_B
3 Z C DESC_C
to this view?
NAME_X DESCRIPTION_X
A DESC_A
B DESC_B
NAME_Z DESCRIPTION_Z
C DESC_C
For every distinct column, I would like to create a custom row for every distinct value in the 'TYPE' column. In this example, the custom row is created by appending the TYPE value to 'NAME_' and 'DESCRIPTION_'.
Thanks!

Try this:
create view vwTestDistinctData
as
select [type], [Description]
from testdistinctdata
union all
select
'NAME_' + [type] as [Type],
'DESCRIPTION_' + [type] as [Description]
from testdistinctdata
group by [type]
go

Edit: Return some meta data from the view:
alter view dbo.yourView
as
with c_Distinct([type])
as ( select distinct [Type]
from dbo.yourTable
)
select [Sort] = 0,
[Type],
Name,
[Description]
from dbo.yourTable
union all
select [Sort] = 1,
[Type],
'NAME_'+[Type],
'DESCRIPTION_'+[Type]
from c_Distinct
And then perform the ordering when selecting from the View:
select *
from yourView
order by [Type] asc, [Sort] desc

Related

How to create SQL Server tree in one row?

create table Test
(
Id int identity,
Name varchar(50) not null,
SName varchar(50) null,
ParentId int null
)
insert into Test
values ('aaa', 'bbb', null), ('adf', '22b', null), ('aad', 'bbsd',2),('asdsaa', 'bf', 3),('sdfs','sdf',3),('iopio','uiopio',3)
select * from Test
I have a table with parentid, and I want to get something like that
Name SName "aaa" "bbb" , "adf" {"aad":{"asdsaa":"bf"}}
from the selected values
I have been trying to get it with a recursive query, but...
WITH tree_view AS
(
SELECT
Id, ParentId, Name, SName
FROM
Test
UNION ALL
SELECT
parent.Id, parent.ParentId, parent.Name, parent.SName
FROM
Test parent
JOIN
tree_view tv ON parent.ParentId = tv.Id
)
SELECT DISTINCT *
FROM tree_view
I want to get this
if the parent id is not null, in one sell I want to get Name:SName in Sname field
I think you need to use PIVOT:
Below is similar solution which you can try
Sample Table creation:
CREATE TABLE [dbo].[TestTable](
[Id] [int] NULL,
[Name] [nvarchar](50) NULL,
[Description] [nvarchar](50) NULL)
Inserting sample values:
INSERT INTO TestTable VALUES (1,'Test1a','TestDesc1a')
INSERT INTO TestTable VALUES (2,'Test1b','TestDesc1b')
INSERT INTO TestTable VALUES (3,'Test2a','TestDesc2a')
INSERT INTO TestTable VALUES (4,'Test2b','TestDesc2b')
Query to get the desired output using Pivot:
SELECT 'Name' AS [Column], [1], [2],[3],[4]
FROM
(SELECT Name, id from TestTable) AS ST
PIVOT
(Max(Name) FOR ID IN ([1], [2],[3],[4])) AS PT
UNION
SELECT 'Description' AS [Column], [1], [2],[3],[4]
FROM
(SELECT id,[Description] from TestTable) AS ST
PIVOT
(Max([Description]) FOR ID IN ([1], [2],[3],[4])) AS PT
ORDER BY [Column] DESC
OutPut:
Column 1 2 3 4
Name Test1a Test1b Test2a Test2b
Description TestDesc1a TestDesc1b TestDesc2a TestDesc2b
Hope this helps to solve your question.
If you ar sure any parent leads to only 1 leaf, it's possible to generate the JSON you want by playing only with string manipulation:
with rel_hier(id, parentid, name, sname, js) as (
select m.id, m.parentid, m.name, m.sname,
CAST(CASE WHEN m.parentid IS NULL THEN
CONCAT('"' , m.sname , '"') ELSE
CONCAT('{"' , m.name , '" : "' , m.sname , '"}') END AS VARCHAR(2000))
js
from Test m
where not exists(select 1 from Test t1 where t1.parentid = m.id)
union all
select m.id, m.parentid, m.name, m.sname,
CAST(CASE WHEN m.parentid IS NULL THEN r.js ELSE CONCAT('{"' , m.name ,
'" : ' , r.js , '}') END AS VARCHAR(2000)) js
from rel_hier r
join Test m on m.id = r.parentid
)
SELECT name, js as sname FROM rel_hier r
WHERE parentid IS NULL
;
name sname
aaa "bbb"
adf {"aad" : {"asdsaa" : "bf"}}
But you should also better explain the substitution rules for a number of levels > 3.

PIVOT without aggregate function ID as column names

I have a query
SELECT a.Name, b.Date FROM
Table1 a
JOIN Table2 b on a.deviceID=b.id
where a.date > '2022-10-01'
Which gives me result
Name Date
A1 '2022-10-01 12:13'
A2 '2022-10-02 14:15'
A2 '2022-10-02 15:16'
A5 '2022-10-03 16:19'
etc.
The result I want to achieve is
A1 A2 A5
'2022-10-01 12:13' '2022-10-02 14:15''2022-10-03 16:19'
'2022-10-02 15:16'
The perfect result would be to receive only one date of each day. Can I do it with pivot?
Pivoting cannot be done (in T-SQL) without aggregation. As for what you want to achieve, seems you need to PIVOT/conditionally aggregate on the value of Name and group on the value of a ROW_NUMBER.
I, personally, prefer using conditional aggregation over the restrictive PIVOT operator, but I have included examples of both:
USE Sandbox;
GO
CREATE TABLE #YourData (Name char(2),
Date datetime2(0));
GO
INSERT INTO #YourData (Name,
Date)
VALUES('A1','2022-10-01T12:13:00'),
('A2','2022-10-02T14:15:00'),
('A2','2022-10-02T15:16:00'),
('A5','2022-10-03T16:19:00');
GO
WITH RNs AS(
SELECT [Name],
[Date],
ROW_NUMBER() OVER (PARTITION BY [Name] ORDER BY [Date]) AS RN
FROM #YourData)
SELECT MAX(CASE [Name] WHEN 'A1' THEN [Date] END) AS A1,
MAX(CASE [Name] WHEN 'A2' THEN [Date] END) AS A2,
MAX(CASE [Name] WHEN 'A5' THEN [Date] END) AS A5
FROM RNs
GROUP BY RN;
GO
SELECT P.A1,
P.A2,
P.A5
FROM (SELECT [Name],
[Date],
ROW_NUMBER() OVER (PARTITION BY [Name] ORDER BY [Date]) AS RN
FROM #YourData)YD
PIVOT(MAX([date])
FOR [Name] IN (A1,A2,A5))P;
GO
DROP TABLE #YourData;
If you want to pivot but keep all your related values in the same field rather than creating fake rows, you can use STRING_AGG. You can replace the "," separator with a CHAR(10), or perhaps an HTML to add a line break. You won't see it in SSMS but depending on your front end, that would render the two items, one under each other.
DROP TABLE IF EXISTS #YourData
CREATE TABLE #YourData (Name char(2),
Date datetime2(0));
GO
INSERT INTO #YourData (Name,
Date)
VALUES('A1','2022-10-01T12:13:00'),
('A2','2022-10-02T14:15:00'),
('A2','2022-10-02T15:16:00'),
('A5','2022-10-03T16:19:00');
GO
DECLARE #Seperator VARCHAR(10) = ' , '
SELECT
[A1] , [A2], [A3], [A4], [A5]
FROM
(
SELECT name, STRING_AGG(date,#Seperator) AS Dates
FROM #YourData
GROUP BY Name
) AS SourceTable
PIVOT
(
MAX(Dates)
FOR Name IN ([A1], [A2], [A3], [A4], [A5])
) AS PivotTable;

Pivot a table in SQL without an aggregate

I have a table with the following format:
ID CODE NAME VALUE
p1 p deflect Yes
a1 d source Prim
p1 p source Dim
I want to pivot to have the following:
ID CODE deflect source
p1 p Yes DIM
a1 d NULL Prim
This is my current code:
SELECT *
from
(
select [ID], [CODE], [NAME], [VALUE]
FROM [DATABASE].[dbo].[TABLE]
) SOURCE_TABLE
pivot
(
max(VALUE)
for [NAME] in ('deflect', 'source')
) PIVOT_TABLE;
But I'm getting:
Incorrect syntax near 'deflect'.
How would you write the pivot code for this?
Why don't you use the conditional aggregation as follows:
select [ID], [CODE],
max(case when [NAME] = 'deflect' then [VALUE] end) as deflect,
max(case when [NAME] = 'source' then [VALUE] end) as source_
FROM [DATABASE].[dbo].[TABLE]
group by [ID], [CODE]

Improve SQL Query to find redundant data

the following shows my sample dataset
PatientID PatientName
XXX-037070002 Riger, Jens^Wicki
XXX-037070002 Riger^Wicki
XXX-10052 Weier,Nicole^Peggy
XXX-10052 Weier,Nicole^Peppy
XXX-23310 Rodem^Sieglinde
XXX-23310 Sauberger, Birgit^Finja
XXX-23343 Je, Ronny^Wilma
XXX-23343 Jer, Ronny^Wilma
XXX-2349 Kel,Andy^Juka
XXX-2349 Kel^Juka
XXX-2998 Hel, Frank
XXX-2998 Hel,Frank^Fenris
XXX-3188 Mey, Marion
XXX-3188 Mey, Marion^Paula
XXX-3188 Schulz^Roma
XXX-3218 Böntgen-Simnet,Dr. Regine^Cara
XXX-3218 Simnet,Dr. Regine^Cara
XXX-3826 Mertes, Bernd Uwe^Ellie
XXX-3826 Mertes,Bernd^Ellie
XXX-3826 Mertes^Ellie
This is the query I got from my last request:
with d as
(
select distinct
patid,
patname
from dicomstudys
)
select *
from d
where d.patid in
(
select d.patid
from d
group by d.patid
having count(*) > 1
)
Now I want to adjust the query that only the following data get's an output:
PatientID PatientName
XXX-23310 Rodem^Sieglinde
XXX-23310 Sauberger, Birgit^Finja
XXX-23343 Je, Ronny^Wilma
XXX-23343 Jer, Ronny^Wilma
XXX-3188 Mey, Marion
XXX-3188 Mey, Marion^Paula
XXX-3188 Schulz^Roma
XXX-3218 Böntgen-Simnet,Dr. Regine^Cara
XXX-3218 Simnet,Dr. Regine^Cara
Last names are either seperated with a ',' or '^' . If last names are the same for the same PatientID then I dont want them being displayed. I tried fiddling with a sub select statement featuring a combination of CHARINDEX commands and others but my SQL syntax knowledge is very limited with the complexity of the request.
Please also note that for the case for XXX-3188 has two datasets with the same last name but also another dataset with a complete new patientName and thus it needs to be in the output.
Try this:
DECLARE #DataSource TABLE
(
[ID] VARCHAR(32)
,[Name] VARCHAR(256)
);
INSERT INTO #DataSource ([ID], [Name])
VALUES ('XXX-037070002', 'Riger, Jens^Wicki')
,('XXX-037070002', 'Riger^Wicki')
,('XXX-10052', 'Weier,Nicole^Peggy')
,('XXX-10052', 'Weier,Nicole^Peppy')
,('XXX-23310', 'Rodem^Sieglinde')
,('XXX-23310', 'Sauberger, Birgit^Finja')
,('XXX-23343', 'Je, Ronny^Wilma')
,('XXX-23343', 'Jer, Ronny^Wilma')
,('XXX-2349', 'Kel,Andy^Juka')
,('XXX-2349', 'Kel^Juka')
,('XXX-2998', 'Hel, Frank')
,('XXX-2998', 'Hel,Frank^Fenris')
,('XXX-3188', 'Mey, Marion')
,('XXX-3188', 'Mey, Marion^Paula')
,('XXX-3188', 'Schulz^Roma')
,('XXX-3218', 'Böntgen-Simnet,Dr. Regine^Cara')
,('XXX-3218', 'Simnet,Dr. Regine^Cara')
,('XXX-3826', 'Mertes, Bernd Uwe^Ellie')
,('XXX-3826', 'Mertes,Bernd^Ellie')
,('XXX-3826', 'Mertes^Ellie');
WITH DataSource AS
(
SELECT [ID]
,[Name]
,COUNT(*) OVER (PARTITION BY [ID], LTRIM(RTRIM(SUBSTRING([Name], 0, CHARINDEX(',', REPLACE([Name], '^', ',')))))) AS [ID_Name_Count]
,COUNT(*) OVER (PARTITION BY [ID]) AS [ID_Count]
,LTRIM(RTRIM(SUBSTRING([Name], 0, CHARINDEX(',', REPLACE([Name], '^', ','))))) AS [FamilyName]
FROM #DataSource
)
SELECT [ID]
,[Name]
FROM DataSource
WHERE [ID_Name_Count] = 1
AND [ID_Count] = 2
OR [ID] IN
(
SELECT [ID]
FROM DataSource
GROUP BY [ID]
HAVING COUNT(DISTINCT [FamilyName]) > 1
);
Тhe solution is pretty easy. Here are the interesting parts:
replace the ^ with , in order to simplify the last name extraction
extract the last name and calculation count based on ID and last name
in the final select check for unique id-last name pairs with id count equal to 2 and add ids with more then one unique family names (your special case)
You can try something like that:
Test data
drop table if exists #Patient;
create table #Patient (
PatientID varchar(20),
PatientName varchar(50)
);
insert into #Patient(PatientID,PatientName)
values ('XXX-037070002' ,'Riger, Jens^Wicki'),
('XXX-037070002' ,'Riger^Wicki'),
('XXX-10052' ,'Weier,Nicole^Peggy'),
('XXX-10052' ,'Weier,Nicole^Peppy'),
('XXX-23310' ,'Rodem^Sieglinde'),
('XXX-23310' ,'Sauberger, Birgit^Finja'),
('XXX-23343' ,'Je, Ronny^Wilma'),
('XXX-23343' ,'Jer, Ronny^Wilma'),
('XXX-2349' ,'Kel,Andy^Juka'),
('XXX-2349' ,'Kel^Juka'),
('XXX-2998' ,'Hel, Frank'),
('XXX-2998' ,'Hel,Frank^Fenris'),
('XXX-3188' ,'Mey, Marion'),
('XXX-3188' ,'Mey, Marion^Paula'),
('XXX-3188' ,'Schulz^Roma'),
('XXX-3218' ,'Böntgen-Simnet,Dr. Regine^Cara'),
('XXX-3218' ,'Simnet,Dr. Regine^Cara'),
('XXX-3826' ,'Mertes, Bernd Uwe^Ellie'),
('XXX-3826' ,'Mertes,Bernd^Ellie'),
('XXX-3826' ,'Mertes^Ellie');
My solution
with q1 as (
select
PatientID,
PatientName,
case when CHARINDEX(',',REPLACE( PatientName, '^',',')) > 0
then LEFT(PatientName,CHARINDEX(',',REPLACE( PatientName, '^',','))-1)
else PatientName end as FullName
from #Patient
) ,
q2 as (
select PatientID
from q1
group by PatientID having COUNT(1) > 1 and COUNT(DISTINCT FullName) > 1 )
select t.PatientID,t.PatientName
from #Patient t join q2 on t.PatientID = q2.PatientID;

Updating column values of a table with column values from a different table

I have a table (Table 1) with columns FN, LN, MN and eleven other columns. I have another table (Table 2) which has FN, LN, MN (only three columns).
I want to update my values FN, LN and MN in the Table 1 with the values FN, LN and MN from Table 2.
I do NOT need to Join on the basis of any other common column.
Please let me know if there is a way to do it.
I cannot use a SELECT * INTO statement because the structure of the two tables is not the same. Table 1 has 11 odd columns and Table 2 has 3 columns.
If Table 1 contains:
[FN] [LN] [MN] [A] [B] [C]
--------------------------------------------------------
hello world 123 something else 456
other row 45 demo data 789
something else 456 NULL NULL 999
and Table 2 contains:
[FN] [LN] [MN]
----------------------------
table two 1
just-a-demo here 2
final row 3
and the expected result is:
[FN] [LN] [MN] [A] [B] [C]
--------------------------------------------------------
table two 1 something else 456
just-a-demo here 2 demo data 789
final row 3 NULL NULL 999
you can first get the expected result like this;
select [S].[FN], [S].[LN], [S].[MN], [F].[A], [F].[B], [F].[C] from
(
select top 10
row_number() over (order by (select 1)) as [Id], [A], [B], [C]
from [Table 1]
) as [F]
left join
(
select top 10
row_number() over (order by (select 1)) as [Id], [FN], [LN], [MN]
from [Table 2]
) as [S]
on [F].[Id] = [S].[Id]
Note:
The top 10: you may need to remove this, but you must be sure both tables return the same number of rows. If they don't, there is no way to do 1:1 mapping according to the information you gave in the comments.
row_number() over (order by (select 1)) simply puts "1" for the first returned row, "2" for the next one, etc. This is a weird way to join the results from two tables: I would expect to have an actual ID in both tables, or something which enables to do an actual join.
You can then insert it into a temporary table:
what you can do is to use the insert into with a subquery select like this:
insert into [TempTable] ([FN], [LN], [MN], [A], [B], [C])
select [S].[FN], [S].[LN], [S].[MN], [F].[A], [F].[B], [F].[C] from
(
select top 10
row_number() over (order by (select 1)) as [Id], [A], [B], [C]
from [Table 1]
) as [F]
left join
(
select top 10
row_number() over (order by (select 1)) as [Id], [FN], [LN], [MN]
from [Table 2]
) as [S]
on [F].[Id] = [S].[Id]
and then replace [Table 1] by [TempTable].
Is this the way that you are looking for.
Declare #table1 Table
(id int identity(1,1),
FN varchar(15),
LN varchar(15),
MN varchar(15),
Flag1 int default(1),
Flag2 int default(1),
Flag3 int default(0))
Declare #table2 Table
(id int identity(1,1), FN varchar(15), LN varchar(15), MN varchar(15))
Insert into #table1 (Fn,LN,MN) Values
('A','B','C'),('A','B','D'),('A','X','C')
Insert into #table2 (Fn,LN,MN) Values
('A','B','C'),('A','B','D'),('A','Y','C')
Merge into #table1 A
using #table2 B On
A.FN = B.FN AND
A.LN = B.LN AND
A.MN = B.MN
When Not Matched then
Insert (FN,LN,MN)
Values (B.FN,B.LN,B.MN);
Select * from #table1
Result