Quite few ways of doing this, in essence create a set of all customers/months and then do the count.
The server normally translates queries such as "select * from A left outer join B where B.xx = yy" to an inner join.
😎
/* For lack of other data */
USE AdventureWorks2012;
GO
;WITH ORDER_MONTH AS
(
/* All Order Months */
SELECT DISTINCT
(YEAR(SOH.OrderDate) * 100) + MONTH(SOH.OrderDate) AS ISO_MONTH
FROM Sales.SalesOrderHeader SOH
)
,CUST_MONTH AS
(
/* All Customer Month combinations */
SELECT
SACU.CustomerID
,OM.ISO_MONTH
FROM Sales.Customer SACU
CROSS APPLY ORDER_MONTH OM
)
,CUST_MONTH_COUNT AS
(
SELECT
ROW_NUMBER() OVER
(
PARTITION BY
CM.CustomerID
,CM.ISO_MONTH
ORDER BY
(SELECT NULL)
) AS CUMO_RID
,CM.CustomerID
,CM.ISO_MONTH
,ISNULL(COUNT(SOH.SalesOrderID) OVER
(
PARTITION BY
CM.CustomerID
,YEAR(SOH.OrderDate), MONTH(SOH.OrderDate)
),0) AS ORDER_COUNT
FROM CUST_MONTH CM
LEFT OUTER JOIN Sales.SalesOrderHeader SOH
ON CM.CustomerID = SOH.CustomerID
AND CM.ISO_MONTH = (YEAR(SOH.OrderDate) * 100) + MONTH(SOH.OrderDate)
)
SELECT
CMC.CustomerID
,CMC.ISO_MONTH
,CMC.ORDER_COUNT
FROM CUST_MONTH_COUNT CMC
WHERE CMC.CUMO_RID = 1
AND CMC.ISO_MONTH = 200803;