SQL combine multiple records into one row - sql-server

I have a table pulling userid's and their personal and work e-mails. I'd like to have one line per id showing both types of e-mails, but I can't figure out how to do that.
declare #t table(NPI int, email varchar(50), emailtype varchar(50))
insert into #t
values(1, 'john#home', 'personal'), (1, 'john#work', 'work');
This is the query I've written so far, which puts this on 2 separate rows:
select npi, case when emailtype = 'personal' then email end as personalemail,
case when emailtype = 'work' then email end as workemail
from #t;
Current Output:
npi personalemail workemail
1 john#home NULL
1 NULL john#work
What I'd like to see is:
npi personalemail workemail
1 john#home john#work
How can I do this?

This has been asked and answered around here about a million times. It is called conditional aggregation or crosstab. It is faster to write an answer than find one. As an alternative you could use PIVOT but I find the syntax a bit obtuse for me.
select NPI
, max(case when emailtype = 'personal' then email end) as PersonalEmail
, max(case when emailtype = 'work' then email end) as WorkEmail
from #t
group by NPI

Use pivot
SELECT
*
FROM #T
PIVOT
(
MAX(email)
FOR EmailType IN
(
personal,work
)
)Q

Related

How to group by with NULL values

What would be the simplest way to group by when NULL values?
declare #MyTable Table (ID int, Name varchar(50),Coverage varchar(50), Premium money)
insert into #MyTable values (1,'Robert', 'AutoBI', 100),
(1,'Robert', NULL, 300),
(2,'Neill','AutoBIPD',150),
(2,'Neill','AutoBI',200),
(3,'Kim', 'Collision',50),
(3,'Kim',NULL,100),
(4,'Rick','AutoBI',70),
(5,'Lukasz','Comprehensive',50),
(5,'Lukasz','NULL',25)
select ID,
Name,
Coverage,
sum(Premium) as Premium
from #MyTable
group by ID
,Name
,Premium
,Coverage
The outcome looks like this:
As you can see there is NULL value for name 'Robert'.
How can I have summed premium ($400) and only one line without NULL Coverage?
But I need to make it look like this:
I cannot use MAX() function in this case.
This solution assumes that NULL will be grouped to one "random" NOT NULL value within ID/Name. If more than single value is poissible then this query won't return stable result sets between executions:
select ID,
Name,
ISNULL(m1.Coverage, sub.Coverage) AS Coverage,
sum(Premium) as Premium
FROM #MyTable m1
cross apply (SELECT TOP 1 m2.Coverage FROM #MyTable m2 WHERE Coverage IS NOT NULL
AND m1.ID = m2.ID AND m1.Name = m2.Name) sub
group by ID
,Name
,ISNULL(m1.Coverage, sub.Coverage);
Rextester Demo

SELECT THAT ALSO SET TO VARIABLE

I just got a job to a company to maintain an existing program in the company.
Now I need to get a data from table.
So I need to do a select from a table, the table column dateBegin maybe null maybe not, so if datebegin is null i get the datebegin2. and dateto is added 1 year from the datebegin if is null or <= datebegin.
All I can do now is using case when else to get the right value. But its too long for the code and hard to read. How can I achieve the code right below? thanks for your response...
declare #val smalldatetime
select id
, #val = COALESCE(dateBegin, dateBegin2) as DateBegin
, COALESCE(dateTo, dateadd(Y, 1, #val)) As DateTo
from TblL
You cannot mix this, but you can use APPLY to row-wise compute additional columns. These columns can be used within the statement similar to a variable:
Cannot test this, but this should be equivalent to your attempt:
select id
, A.DateBegin
, COALESCE(dateTo, dateadd(Y, 1, A.DateBegin)) As DateTo
from TblL
CROSS APPLY(SELECT COALESCE(dateBegin, dateBegin2)) AS A(DateBegin)
A working example to illustrate the idea
DECLARE #tbl TABLE(ID INT IDENTITY, SomeString VARCHAR(500));
INSERT INTO #tbl VALUES('Find the #value# between the "#"')
,('One more #example#');
SELECT t.ID
,t.SomeString
,A.FirstHash
,B.SecondHash
,SUBSTRING(t.SomeString,A.FirstHash+1,C.FragmentLength) AS Fragment
FROM #tbl AS t
CROSS APPLY(SELECT CHARINDEX('#',t.SomeString)) AS A(FirstHash)
CROSS APPLY(SELECT CHARINDEX('#',t.SomeString,A.FirstHash+1)) AS B(SecondHash)
CROSS APPLY(SELECT B.SecondHash-A.FirstHash-1) AS C(FragmentLength);
Returns
ID SomeString 1.# 2.# Fragment
1 Find the #value# between the "#" 10 16 value
2 One more #example# 10 18 example
The same query without this trick would be something like this
SELECT t.ID
,t.SomeString
,CHARINDEX('#',t.SomeString)
,CHARINDEX('#',t.SomeString,CHARINDEX('#',t.SomeString)+1)
,SUBSTRING(t.SomeString,CHARINDEX('#',t.SomeString)+1,CHARINDEX('#',t.SomeString,CHARINDEX('#',t.SomeString)+1)-CHARINDEX('#',t.SomeString)-1) AS Fragment
FROM #tbl AS t

SQL server OPENJSON with multiple where on a filed

I have a table with a json field. The json schema is the same for all records.
I want to get just 2 products with red or blue color and with brand 1.
I tried the below query but I know that's not working:
SELECT [Id], [JName], [JValue]
FROM [Product]
CROSS APPLY OPENJSON([Json])
WITH ([JName] NVARCHAR(50) '$.name', [JValue] NVARCHAR(50) '$.value')
WHERE
(CASE WHEN [JName]=N'color' AND [JValue] IN (N'red', N'red') THEN 1 ELSE 0 END) &
(CASE WHEN [JName]=N'brand' AND [JValue] IN (N'brand 1') THEN 1 ELSE 0 END) = 1
so, how should I write this query?
I am a bit unsure of what you are asking:
How to get data matching your criteria (red or blue and brand1), since your posted query will not get what you want.
OR
How to do an OFFSET for pagination purposes.
OR
Both.
Anyway, after my initial comment above (but before your reply), I wrote a query which would give you what you want (red or blue and Brand1).
After your reply I modified the query to do OFFSET as well:
;WITH prod
AS
(
SELECT [Id], [JName], [JValue]
FROM dbo.tb_Product
CROSS APPLY OPENJSON([Json])
WITH ([JName] NVARCHAR(50) '$.name', [JValue] NVARCHAR(50) '$.value')
)
SELECT p1.Id, p1.JValue AS Color, p2.JValue AS Brand
FROM prod p1
JOIN prod p2
ON p1.Id = p2.Id
WHERE p1.JName = 'Color'
AND p1.JValue IN ('red', 'blue')
AND p2.JName = 'Brand'
AND p2.JValue IN ('Brand1')
ORDER BY p1.ID
OFFSET 0 ROWS
FETCH NEXT 2 ROWS ONLY;
I hope this is somewhat what you want.

Speeding up my query using sql server 2008.Alternative to cross apply

I have a function that is performing very slow.I am working a database that I need to migrate data and I have NO control over!.
Ideally I would like to use a view directly since this function is called by a view ,but I could only seem to be able to do it by calling a function.
===
A view should return whatever is in the dummytable by orderNo.If an orderNo has a paymenttype of "Interest" than the balance should be interest,if "tax" should be tax
In real life I will have 200000 rows and more,by using cross apply it seems to slow down quite a lot.
Is there a better way to get the data rather than using CrossApply?
Noddy Sample here(data and datatypes are just fictious for semplicity of the example)
CREATE DATABASE DummyDB
GO
use DummyDB
IF object_id(N'DummyTable', 'U') IS NOT NULL
DROP TABLE DummyTable
GO
CREATE TABLE DummyTable
(
Id int,
OrderNo varchar(255),
PaymentType varchar(255),
Credit varchar(255),
Debit varchar(255),
Balance varchar(255)
)
GO
INSERT INTO [dbo].[DummyTable]([Id], [OrderNo], [PaymentType], [Credit], [Debit], [Balance])
SELECT 1, N'200', N'Interest', N'10', N'5', N'5' UNION ALL
SELECT 2, N'201', N'Deposit', N'400', N'30', N'370' UNION ALL
SELECT 3, N'202', N'Tax', N'20', N'10', N'10' UNION ALL
SELECT 4, N'202', N'Tax', N'50', N'10', N'10'
--my sample attempt not performing
use DummyDB
select * from DummyTable
Declare #OrderNo int
set #OrderNo=202
SELECT
Tax=tx.Tax,
Interest=tx1.Interest,
Deposit=tx2.Deposit
FROM DummyTable T1
CROSS APPLY(SELECT
Tax=sum(cast(T2.Balance as money))
FROM DummyTable T2
WHERE T2.OrderNo=#OrderNo
AND PaymentType='Tax')as tx
CROSS APPLY(select
Interest=sum(cast(T2.Balance as money))
FROM DummyTable T2
WHERE T2.OrderNo=#OrderNo
AND PaymentType='Interest')as tx1
CROSS APPLY(select
Deposit=sum(cast(T2.Balance as money))
FROM DummyTable T2
WHERE T2.OrderNo=#OrderNo
AND PaymentType='Deposit')as tx2
WHERE T1.OrderNo=#OrderNo
Any Suggestion of using something more efficient than cross apply?
Many thanks
This will do almost the same as your sample query. It will give you one row with the result where your query will repeat the values for all rows that match #OrderNo.
select sum(case when T1.PaymentType = 'Tax' then cast(T1.Balance as money) else 0 end) as Tax,
sum(case when T1.PaymentType = 'Interest' then cast(T1.Balance as money) else 0 end) as Interest,
sum(case when T1.PaymentType = 'Deposit' then cast(T1.Balance as money) else 0 end) as Deposit
from DummyTable as T1
where T1.OrderNo = #OrderNo
BTW, you should make sure that the data type for OrderNo in the table is the same as the variable #OrderNo. It looks like you are dealing with integers so you should change the table. If that is not possible for you then you need to change #OrderNo to varchar(255) if you want to use an index on OrderNo.

Arrange rows in T-SQL

How to arrange rows manually in T-SQL?
I have a table result in order like this:
Unknown
Charlie
Dave
Lisa
Mary
but the expected result is supposed to be:
Charlie
Dave
Lisa
Mary
Unknown
edited:
My whole query is:
select (case when s.StudentID is null then 'Unknown' else s.StudentName end) as StudentName from Period pd full join Course c on pd.TimeID = c.TimeID full join Student s on c.StudentID = s.StudentID
group by s.StudentName, s.StudentID
order by case s.StudentName
when 'Charlie' then 1
when 'Dave' then 2
when 'Lisa' then 3
when 'Mary' then 4
when 'Unknown' then 5
end
but it didn't work. I think the problem root is because Unknown is from NULL value, as I wrote in that query that when StudentID is null then change "NULL" to "Unknown". Is this affecting the "stubborn" order of the result? By the way I also have tried order by s.StudentName asc but also didn't work.
Thank you.
Try the following...
SELECT os.StudentName
FROM ( SELECT CASE WHEN s.StudentID IS NULL THEN 'Unknown'
ELSE s.StudentName
END AS StudentName
FROM Period pd
FULL JOIN Course c ON pd.TimeID = c.TimeID
FULL JOIN Student s ON c.StudentID = s.StudentID
GROUP BY s.StudentName ,
s.StudentID
) AS os
ORDER BY os.StudentName
Edit: based on comment...
When I use this, it works fine...notice the Order By has no identifier
declare #tblStudent TABLE (StudentID int, StudentName varchar(30));
insert into #tblStudent values (null, '');
insert into #tblStudent values (1, 'Charlie');
insert into #tblStudent values (2, 'Dave');
insert into #tblStudent values (3, 'Lisa');
insert into #tblStudent values (4, 'Mary');
SELECT CASE WHEN s.StudentID IS NULL THEN 'Unknown'
ELSE s.StudentName
END AS StudentName
FROM #tblStudent s
GROUP BY s.StudentName ,
s.StudentID
ORDER BY StudentName
As I see your rows must be ordered alphabetically, so just add in the end of the query: ORDER BY p.StudentName.
If this not help, please add whole query, so we can find out the problem.
So when I see query I can explain. You try to sort by column p.StudentName. This column contains NULL. Try to sort by StudentName without p in front. This is alias of the expression which contains Unknown.
just put the following clause in you SQL statement:
order by p.StudentName
Sql server will order the column alphabetically.

Resources