Combine multiple rows into 1 row - sql

Say for example I have a table that contains a description of a customer's activities while in a cafe. (Metaphor of the actual table I am working on)
Customer Borrowed Book Ordered Drink Has Company
1 1
1 1
1 Yes
2 1
3 1
3 Yes
4 1 1
4 1
I wish to combine the rows in this way
Customer Borrowed Book Ordered Drink Has Company
1 1 1 Yes
2 1
3 1 Yes
4 1 2
I did self join with coalesce, but it did not give my desired results.

You can do this by group by,
select Customer,sum([borrowed book]), sum([ordered drink]), max([has company])
from customeractivity group by Customer

As per your comment, initial table is a temp table,
Try to make the result as a cte result, then do aggregation on that, like the below query.
; WITH cte_1
AS
( //your query to return the result set)
SELECT customer,sum([borrowed book]) BorrowedBook,
sum([ordered drink]) OrderedDrink,
max([has company]) HasCompany
FROM cte_1
GROUP BY Customer

Use Group By:
DECLARE #tblTest as Table(
Customer INT,
BorrowedBook INT,
OrderedDrink INT,
HasCompany BIt
)
INSERT INTO #tblTest VALUES
(1,1,NULL,NULL)
,(1,NULL,1,NULL)
,(1,NULL,NULL,1)
,(2,NULL,1,NULL)
,(3,NULL,1,NULL)
,(3,NULL,NULL,1)
,(4,1,1,NULL)
,(4,NULL,1,NULL)
SELECT
Customer,
SUM(ISNULL(BorrowedBook,0)) AS BorrowedBook,
SUM(ISNULL(OrderedDrink,0)) AS OrderedDrink,
CASE MIN(CAST(HasCompany AS INT)) WHEN 1 THEN 'YES' ELSE '' END AS HasCompany
FROM #tblTest
GROUP BY Customer

Not sure, why you are getting error with group by.
Your coalesce should be correct. Refer below way.
Select customer
, case when [borrowed] = 0 then NULL else [borrowed] end as [borrowed]
, case when [ordered] = 0 then NULL else [ordered] end as [ordered]
, case when [company] = 1 then 'Yes' end as company
from
(
Select customer,
coalesce(
case when (case when borrowed = '' then null else borrowed end) = 1 then 'borrowed' end,
case when (case when ordered = '' then null else ordered end) = 1 then 'ordered' end,
case when (case when company = '' then null else company end) = 'Yes' then 'company' end
) val
from Table
) main
PIVOT
(
COUNT (val)
FOR val IN ( [borrowed], [ordered], [company] )
) piv
OUTPUT:
customer | borrowed | ordered | company
---------------------------------------
1 1 1 Yes
2 NULL 1 NULL
3 NULL 1 Yes

Related

Checking if all values for user_id IS NOT NULL

I have dataset which looks like this:
UserID AccountID CloseDate
1 1000 14/3/2022
1 2000 16/3/2022
2 1000 NULL
2 2000 4/3/2022
2 3000 NULL
And I would like to check if within one user_id all of the close dates are not null. In other words if all accounts within user_id are closed. I was trying using MAX or MIN but it is not working as I expected, because it is simply avoiding NULL values. Is there any other function which can check it? Let's say that my output would be another column which will assign 1 when all CloseDates are not null and else 0.
Sample output:
UserID AccountID CloseDate Check
1 1000 14/3/2022 1
1 2000 16/3/2022 1
2 1000 NULL 0
2 2000 4/3/2022 0
2 3000 NULL 0
Use conditional aggregation to explicitly COUNT the rows where the column has the value NULL:
SELECT GroupedColumn,
COUNT(CASE WHEN NullableColumn IS NULL THEN 1 END) AS NullCount
FROM dbo.YourTable
GROUP BY GroupedColumn;
If you want to just have a 1 or 0 just wrap the count in a CASE expression:
CASE COUNT(CASE WHEN NullableColumn IS NULL THEN 1 END) WHEN 0 THEN 1 ELSE 0 END
You can try to use FIRST_VALUE condition window function
SELECT *,
FIRST_VALUE(IIF(CloseDate IS NULL,0,1)) OVER(PARTITION BY UserID ORDER BY CloseDate )
FROM T
sqlfiddle
with dataset as (select 1 as UserId, 1000 as AccountID, '14/3/2022' as CloseDate
union all select 1, 2000, '16/3/2022'
union all select 2, 1000, NULL
union all select 2, 2000, '4/3/2022'
union all select 2, 3000, NULL)
select userid from dataset
group by userid
having sum(case when closedate is null then 1 else 0 end) = 0;
select d.*, iif(chk>0, 0, 1) chk
from d
outer apply (
select UserId, COUNT(*) CHK
from d dd
WHERE d.UserId = dd.UserId
and dd.CloseDate IS NULL
group by UserId
) C
You can also use "exists". e.g. :
select y.UserID, y.AccountID, y.CloseDate,
-- [Check]: returns 0 if there is a row in the table for the
-- UserID where CloseDate is null, else 1
(case when exists(select * from YourTable y2 where y2.UserID = y.UserID
AND y2.CloseDate is null) then 0 else 1 end) as [Check]
from YourTable y

MSSQL How to handle null values to count them within pivot

I'm here again because I've another problem with pivot table that concerns with null values.
IF OBJECT_ID(N'tempdb..#exams') IS NOT NULL
BEGIN
DROP TABLE #exams
END
GO
create table #exams (
id uniqueidentifier,
exam nvarchar(max),
technician nvarchar(max)
)
insert into #exams
values
(newid(),'Esame1','Tecnico1'),
(newid(),'Esame2','Tecnico1'),
(newid(),'Esame1','Tecnico2'),
(newid(),'Esame3','Tecnico1'),
(newid(),'Esame3','Tecnico2'),
(newid(),'Esame3','Tecnico3'),
(newid(),'Esame3','Tecnico1'),
(newid(),'Esame1',NULL)
I have to handle in some way null values in my reports.
With sum case clause I could do simply in this way:
select
exam,
sum(case when technician = 'Tecnico1' then 1 else 0 end) as Tecnico1,
sum(case when technician = 'Tecnico2' then 1 else 0 end) as Tecnico2,
sum(case when technician = 'Tecnico3' then 1 else 0 end) as Tecnico3,
sum(case when technician is null then 1 else 0 end) as Unknown
from #exams
group by exam
order by exam
exam
Tecnico1
Tecnico2
Tecnico3
Unkwnon
Esame1
1
1
0
1
Esame2
1
0
0
0
Esame3
2
1
1
0
but using pivot table (thanks again to Tole1010) null values stay outside from my pivot
select * from (
select id,exam,
technician
from #exams
) as t
pivot
( count(id)
for technician in (Tecnico1,Tecnico2,Tecnico3)
) as t
and I get only:
exam
Tecnico1
Tecnico2
Tecnico3
Esame1
1
1
0
Esame2
1
0
0
Esame3
2
1
1
Is there a way to add a column to count those null values using pivot syntax?
You would typically replace null with something else, that does not otherwise appear in the column:
select * from (
select id, exam,
coalesce(technician , 'Unknown') as technician
from #exams
) as t
pivot (
count(id)
for technician in (Tecnico1,Tecnico2,Tecnico3, Unknown)
) as t
Demo on DB Fiddlde:
exam
Tecnico1
Tecnico2
Tecnico3
Unknown
Esame1
1
1
0
1
Esame2
1
0
0
0
Esame3
2
1
1
0

SQL count of values from different columns

I've got a Dog table. Each dog has Breed and can have 0 to 2 photos. I need to recieve count of photos of all dogs for each breed: table with BreedId and matching PhotosCount. So result table should be:
BreedID|PhotosCount
-------------------
1 |3
-------------------
2 |1
-------------------
This should do the trick:
SELECT BreedID AS B, COUNT(Photo1) + COUNT(Photo2) AS C
FROM Dog
GROUP BY BreedID
COUNT aggregate function simply doesn't take into consideration NULL values. If, for a specific BreedID, all values of either Photo1 or Photo2 are NULL, then COUNT returns 0.
This should work in single scan:
SELECT
BreedID,
SUM(CASE WHEN Photo1 IS NOT NULL THEN 1 ELSE 0 END)
+ SUM(CASE WHEN Photo2 IS NOT NULL THEN 1 ELSE 0 END) [Count]
FROM Table
GROUP BY BreedID
Use Group By and SUM Of Photo1 and Photo2:
Note: If you wants the output for each dog you have to include DogId in group clause.
;WITH T AS
(
SELECT
BreedId,
SUM (CASE ISNULL(Photo1,0) WHEN 1 THEN 1 ELSE 0 END) AS Photo1,
SUM (CASE ISNULL(Photo2,0) WHEN 1 THEN 1 ELSE 0 END) AS Photo2
FROM TableName
Group By BreedId
)
SELECT
BreedId,
SUM(Photo1+Photo2) AS TotalPhoto
FROM T
Or Simply
SELECT
BreedId,
SUM (CASE ISNULL(Photo1,0) WHEN 1 THEN 1 ELSE 0 END + CASE ISNULL(Photo2,0) WHEN 1 THEN 1 ELSE 0 END) AS TotalPhoto
FROM TableName
Group By BreedId
SELECT BreedID AS Breed, COUNT(Photo1) + COUNT(Photo2) AS #ofPhotos
FROM Dog
GROUP BY BreedID;

Order SQL query by the sum of specefic columns

Here is an extract from the fairly large table (SQL Server 2005) I'm querying against:
id (primary key) | account | phone | employee | address
------------------------------------------------------------------
1 | 123 | Y | Y | N
2 | 456 | N | N | N
3 | 789 | Y | Y | Y
I need to only return the rows that have at least one Y in phone, employee, or address (there are about 10 others not shown here). Then I need to order those results by the number of Y's they have in any of the three.
I've tried getting the "tagTotal" like this:
SELECT
SUM(
CASE WHEN [phone] = 'Y' THEN 1 ELSE 0 END
+ CASE WHEN [employee] = 'Y' THEN 1 ELSE 0 END
+ CASE WHEN [address] = 'Y' THEN 1 ELSE 0 END
)
FROM table
GROUP BY id
this returns:
tagTotal
---------------
2
0
3
I'm at a loss on how to combine this with my existing giant query and order by it without adding each column to the group by at the end.
Since the sum of values you're after is on the same row, you don't need to aggregrate the results, thereby eliminating the need for the group by..
SELECT
CASE WHEN [phone] = 'Y' THEN 1 ELSE 0 END +
CASE WHEN [employee] = 'Y' THEN 1 ELSE 0 END +
CASE WHEN [address] = 'Y' THEN 1 ELSE 0 END as Total
FROM table
You can just do the addition as a column and then order the results. The aggregation seems unnecessary, at least with the sample data in the question. There is only one row per id.
SELECT t.*
FROM (SELECT t.*,
((CASE WHEN [phone] = 'Y' THEN 1 ELSE 0 END) +
(CASE WHEN [employee] = 'Y' THEN 1 ELSE 0 END) +
(CASE WHEN [address] = 'Y' THEN 1 ELSE 0 END)
) as NumYs
FROM table t
) t
WHERE NumYs > 0
ORDER BY NumYs DESC;
Try selecting the ID and ordering by the sum?
SELECT id,
SUM(
CASE WHEN [phone] = 'Y' THEN 1 ELSE 0 END
+ CASE WHEN [employee] = 'Y' THEN 1 ELSE 0 END
+ CASE WHEN [address] = 'Y' THEN 1 ELSE 0 END
) as numsum
FROM table
ORDER BY numsum
This should work:
select *
from
(
SELECT
id,
SUM(
CASE WHEN [phone] = 'Y' THEN 1 ELSE 0 END
+ CASE WHEN [employee] = 'Y' THEN 1 ELSE 0 END
+ CASE WHEN [address] = 'Y' THEN 1 ELSE 0 END
) tagTotal
FROM table
GROUP BY id
) x
where x.tagTotal <> 0
order by x.tagTotal desc
The inner query is basically yours, with the addition of the Id (which I assume you need) and giving the sum a name. This is then used as the input to the outer query, excluding those with a zero total and sorting with highest sum first.
(Incidentally, this is not a large query. The largest single select statement I have written covered over 250 lines, took 20 minutes to run, and did the daily P&L of a commodity trading company. That was large...)

Checking if the row has the max value in a group

I'm trying get to find out if a row has the max value in a group. Here's really simple example:
Data
VoteCount LocationId UserId
3 1 1
4 1 2
3 2 2
4 2 1
Pseudo-query
select
LocationId,
sum(case
when UserId = 1 /* and has max vote count*/
then 1 else 0
end) as IsUser1Winner,
sum(case
when UserId = 2 /* and has max vote count*/
then 1 else 0
end) as IsUser2Winner
from LocationVote
group by LocationID
It should return:
LocationId IsUser1Winner IsUser2Winner
1 0 1
2 1 1
I also couldn't find a way to generate dynamic column names here. What would be the simplest way to write this query?
You could also do this using a Case statement
WITH CTE as
(SELECT
MAX(VoteCount) max_votes
, LocationId
FROM LocationResult
group by LocationId
)
SELECT
A.LocationId
, Case When UserId=1
THEN 1
ELSE 0
END IsUser1Winner
, Case when UserId=2
THEn 1
ELSE 0
END IsUser2Winner
from LocationResult A
inner join
CTE B
on A.VoteCount = B.max_votes
and A.LocationId = B.LocationId
Try this:
select *
from table t
cross apply (
select max(votes) max_value
from table ref
where ref.group = t.group
)votes
where votes.max_value = t.votes
but if your table is huge and has no propriate indexes performance may be poor
Another way is to get max values by groups into table variable or temp table and then join it to original table.