How to return empty groups in SQL GROUP BY clause
Yes, construct an expression that returns the ordertotal for adhoc only, and 0 for the others, and another one that does the opposite, and sum those expressions. This will include one row per location with two columns one for adhoc and one for Contracted...
SELECT Location,
Sum(Case When Contract_ID Is Null Then OrderTotal Else 0 End) AdHoc,
Sum(Case When Contract_ID Is Null Then 0 Else OrderTotal End) Contracted
FROM Orders
GROUP BY Location
if you reallly want separate rows for each, then one approach would be to:
SELECT Location, Min('AdHoc') ContractStatus,
Sum(Case When Contract_ID Is Null
Then OrderTotal Else 0 End) OrderTotal
FROM Orders
GROUP BY Location
Union
SELECT Location, Min('Contracted') ContractStatus,
Sum(Case When Contract_ID Is Null
Then 0 Else OrderTotal End) OrderTotal
FROM Orders
GROUP BY Location
Order By Location
Adding empty groups to a group by clause (getting all 24 hours of the day represented)
You can use a recursive CTE (or other method) to generate 24 hours:
with hours as (
select 0 as h
union all
select h + 1
from hours
where h + 1 < 24
)
select hours.h as [Hour],
sum(case . . . else 0 end) as [Calls Answered]
from hours left join
[table] t
on hours.h = datepart(hour, [Start Time])
group by hours.h
order by hours.h;
The else 0
is one way to (probably) ensure that sum()
returns 0
instead of NULL
. If you already have an else
, you need to be careful, because some hours might not have any matches in your table.
SQL Group By including empty rows
SELECT pla.PlaceID, COUNT(peo.PersonID)
FROM Places AS pla LEFT OUTER JOIN People as peo ON peo.PlaceID = pla.PlaceID
GROUP BY pla.PlaceID
EDITed question:
Assuming there is always a FooConfig
entry, we'll drop the LEFT JOIN
to that table (as it'll always be there). We can then include the extra criteria in the join to the Foo
table:
SELECT
ft.FooTypeID, COUNT(f.FooID)
FROM FooType as ft
JOIN FooConfig fc ON ft.NotificationConfigID = fc.FooConfigID
LEFT OUTER JOIN Foo f ON ft.FooTypeID = f.FooTypeID AND
DateDiff(day, GetDate(), f.Date) > 0 AND
DateDiff(day, GetDate(), f.Date) < fc.Days
GROUP BY ft.FooTypeID
If the FooConfig
table is optional, then the extra date criteria can't be used (as they would always evaluate to false) - so we'd have to do something like:
SELECT
ft.FooTypeID, COUNT(f.FooID)
FROM FooType as ft
LEFT OUTER JOIN FooConfig fc ON ft.NotificationConfigID = fc.FooConfigID
LEFT OUTER JOIN Foo f ON ft.FooTypeID = f.FooTypeID AND
(
(DateDiff(day, GetDate(), f.Date) > 0 AND
DateDiff(day, GetDate(), f.Date) < fc.Days)
OR
(fc.Days IS NULL)
)
GROUP BY ft.FooTypeID
Return 0 in GROUP BY when COUNT(*) is NULL
So I flipped the aggregates from the edit to my original post and now it's working:
Query
SELECT
CAST(a.IndexedDate as varchar) as dt,
COUNT(EventType) AS Logins
FROM
(
SELECT DISTINCT(IndexedDate)
FROM Table
WHERE IndexedDate > DATEADD(mm, -1, GETDATE())
) a
FULL OUTER JOIN (
SELECT *
FROM Table
WHERE IndexedDate > DATEADD(mm, -1, GETDATE())
AND EventType = 'Login'
) b
ON
a.IndexedDate = b.IndexedDate
GROUP BY
a.IndexedDate
ORDER BY
a.IndexedDate DESC
Results
2016-09-13 41
2016-09-12 31
2016-09-11 0
2016-09-10 0
2016-09-09 15
2016-09-08 36
Note that I had to replace COUNT(*)
with COUNT(EventType)
so it wouldn't count the date from the aggregate which was resulting in a 1.
Include Empty Group in GROUP BY (MYSQL QUERY)
Since your grouping fields seem to have no relation to one another other than via the "things" they are associated with, you will need to synthesize all possible groupings; you can then left join the "things" to that superset. Something like this:
SELECT lvl.lvl_name as Level, slvl.sublvl_name as SubLevel, sta.stat_name as Status
, COUNT(thg.Id_thing) as Thing_Total
FROM tb_level lvl
JOIN tb_sublevel slvl
JOIN tb_status sta
LEFT JOIN tb_things as thg
ON thg.id_lvl=lvl .id_lvl
AND thg.id_slvl=slvl.id_slvl
AND thg.id_stat= sta.id_stat
GROUP BY Level, SubLevel, Status
However, if there are any "things" without some grouping references (like a thing with id_lvl and id_slvl set, but an id_stat that is null), those things will be omitted from the results.
SQL Server group by absorb null and empty values
You can't have a field in the SELECT
statement unless it's part of the GROUP BY clause or used for aggregation. The question and desired output shows that the rows should be grouped by name, which means all other fields (ID,amount, comments) should be aggregated.
The question doesn't specify how the IDs should be aggregated, or which comments should appear. Aggregating strings is only possible using functions like MIN/MAX in all SQL Server versions up to 2016. SQL Server 2017 added STRING_AGG to concatenate strings. In earlier versions people have to use one of many string aggregation techniques that may involve XML or SQLCLR functions.
In SQL Server versions the desired output can be produced by
SELECT MIN(ID) as ID,name,sum(amount) as Amount, max(comment) as comments
from #table1
group by name
This produces the desired output :
ID name Amount comments
1 n1 421762 Hello
2 n2 5810 Bye
This assumes that there is only one non-empty comment. The question doesn't specify something different though.
In SQL Server 2017 multiple comments can be concatenated with STRING_AGG :
SELECT MIN(ID) as ID,name,sum(amount) as Amount, STRING_AGG(comment,' ') as comments
from table1
group by name
Given the question's data, this will also produce the desired output.
ID name Amount comments
1 n1 421762 Hello
2 n2 5810 Bye
Group By Date With Empty Groups
Essentially what you're asking is to join your table to a "table" of dates. The date table would have no gaps and you would group on the date value. So how to create a table of dates?
In SQL for Smarties, it's suggested that you keep a table of integers around for cases when you need a gapless sequence to join to. Then you can select whatever sequence you need by joining your table to it.
So if you had an integer table with values going as many days back from NOW() as required you might do the following:
SELECT DATE_SUB(CURDATE(), INTERVAL i.intvalue DAY) AS thedate,
COUNT(DISTINCT LoginAudit.LoginAuditID) AS logins
FROM i LEFT JOIN dual ON (DATE_SUB(NOW(), INTERVAL i.intvalue DAY)= day)
GROUP BY DATE_SUB(CURDATE(), INTERVAL i.intvalue DAY)
ORDER BY i DESC;
ETA, for mysql:
//create an integer table
create table i(i integer not null primary key);
insert into i values (0),(1),(2) ... (9);
if I need 0-99 consecutive numbers:
SELECT 10*t.i + u.i AS number
FROM i AS u
CROSS JOIN
i AS t
ORDER BY number;
if I need consecutive dates:
SELECT date_sub(curdate(), interval (10*t.i + u.i) DAY) as thedate
FROM i AS u
CROSS JOIN
i AS t
ORDER BY thedate;
Empty GROUP BY in PostgreSQL
To aggregate all rows, you don't need to form groups with GROUP BY
and can can just omit the GROUP BY
clause. The manual:
If there are aggregate functions but no
GROUP BY
clause, the query is
treated as having a single group comprising all the selected rows.
Or (if you are building the query string dynamically) you can use any constant expression like:
...
GROUP BY true
You can't use for this purpose because integer constants serve as positional references (ordinal numbers) in GROUP BY 1
GROUP BY
:
- Select first row in each GROUP BY group?
Related Topics
How to Fix Ora-01427 Single-Row Subquery Returns More Than One Row in Select
How to Have a Tableless Select with Multiple Rows
SQL Server 2008 Cross Tab Query
How to Convert Hh:Mm:Ss to Seconds in SQL Server with More Than 24 Hours
MySQL - Change Date String to Date Type in Place
How to Get Count() and Rows from One SQL Query in SQL Server
Check Constraint on Date of Birth
Left Outer Join and an Additional Where Clause
SQL Server Rounding Error, Giving Different Values
Issues with SQL Comparison and Null Values
SQL Comma Delimted Column => to Rows Then Sum Totals
SQL Server:Export Query as a .Txt File
Anyway for Ado to Read Updated Data from a Read-Only Excel File Before Save? (Vba)
Is SQL Order by Clause Guaranteed to Be Stable ( by Standards)
Passing Dynamic Order by in Stored Procedure