how to capture the sum of null of an attribute(bit_id) by date and company
If what you mean is you want to count how many values are NULL for each date/company, then you can just use a CASE statement wrapped in a SUM:
declare #dataTable table (
recordDate date,
company varchar(100),
attribute bit
)
insert into #dataTable values
('2021-09-28','CompanyA',null),
('2021-09-28','CompanyA',null),
('2021-09-28','CompanyA',1),
('2021-09-28','CompanyB',0),
('2021-09-28','CompanyB',null),
('2021-09-29','CompanyA',0),
('2021-09-29','CompanyB',1),
('2021-09-29','CompanyA',null),
('2021-09-29','CompanyB',null),
('2021-09-29','CompanyB',null),
('2021-09-29','CompanyB',null)
select
recordDate,
company,
sum(case when attribute is null then 1 else 0 end) as countNULLs
from #dataTable
group by recordDate, company
order by recordDate, company
Results:
/------------------------------------\
| recordDate | company | countNULLs |
|------------|----------|------------|
| 2021-09-28 | CompanyA | 2 |
| 2021-09-28 | CompanyB | 1 |
| 2021-09-29 | CompanyA | 1 |
| 2021-09-29 | CompanyB | 3 |
\------------------------------------/
Related
From a stock transaction table I've created following sorted view table to be ready for FIFO:
rowN
date_
stockCode
sign_
amount
unitPrice
1
'2022-01-20'
ABC
in
5
29.20
2
'2022-01-22'
ABC
in
3
32.50
3
'2022-01-23'
ABC
out
7
40.00
4
'2022-01-23'
ABC
out
1
42.00
5
'2022-01-01'
XYZ
in
3
20.50
6
'2022-01-03'
XYZ
out
3
25.00
and I want to create a select query which looks like the previous table with only "out" rows and added cost_of_sales columns which is FIFO cost of that sale. But my knowledge of SQL is limited by just joins and sum over partitions.
The resulting table in my mind should look like this:
rowN
date_
stockCode
sign_
amount
unitPrice
cost_of_sales_uP
cost_of_sales
3
'2022-01-23'
ABC
out
7
40.00
30.1428
211.00
4
'2022-01-23'
ABC
out
1
42.00
32.50
32.50
6
'2022-01-03'
XYZ
out
3
25.00
20.50
61.50
I have no idea how to achieve this. Any help and guidance is appreciated. Result table doesn't have to be exactly like that but the main idea is there.
Thanks!
The following method might not be the fastest.
But it gets the job done.
First the incoming are unfolded into a temp table.
Then, by looping over the outgoing, each incoming unit is assigned to an outgoing on a First-In-First-Out basis.
The final query then uses the results of the temp table to calculate the total & average of the incoming.
IF OBJECT_ID('tempdb..#tmpStock') IS NOT NULL DROP TABLE #tmpStock;
CREATE TABLE #tmpStock (
id int identity primary key,
code varchar(30),
date_in date,
rowN int,
unitPrice decimal(10,2),
rowN_out int
);
--
-- Using a recursive CTE to unfold the incoming for the temp table
--
with RCTE as (
select stockCode, date_, rowN, amount, unitPrice
, 1 as lvl
from stock_transactions
where sign_ = 'in'
union all
select stockCode, date_, rowN, amount, unitPrice
, lvl + 1
from RCTE
where lvl < amount
)
insert into #tmpStock (code, date_in, rowN, unitPrice)
select stockCode, date_, rowN, unitPrice
from RCTE
order by stockCode, date_, rowN
option (maxrecursion 0);
DECLARE #IdOut INT = 1;
DECLARE #RowsOut INT = 0;
DECLARE #code VARCHAR(30);
DECLARE #amount SMALLINT;
DECLARE #date DATE;
DECLARE #rowN INT;
DECLARE #StockOut TABLE (
id int identity primary key,
code varchar(30),
date_out date,
rowN int,
amount smallint
);
insert into #StockOut (code, date_out, rowN, amount)
select stockCode, date_, rowN, amount
from stock_transactions
where sign_ = 'out'
order by stockCode, date_, rowN;
SELECT #RowsOut = COUNT(*) FROM #StockOut;
WHILE #IdOut <= #RowsOut
BEGIN
SELECT
#code = code
, #amount = amount
, #date = date_out
, #rowN = rowN
FROM #StockOut
WHERE id = #IdOut;
;WITH cte_in as (
select *
, rn = row_number() over (order by date_in, rowN)
from #tmpStock
where code = #code
and date_in <= #date
and rowN_out is null
)
UPDATE cte_in
SET rowN_out = #rowN
WHERE rn <= #amount;
SET #IdOut = #IdOut + 1;
END;
select * from #tmpStock
id | code | date_in | rowN | unitPrice | rowN_out
-: | :--- | :--------- | ---: | --------: | -------:
1 | ABC | 2022-01-20 | 1 | 29.20 | 3
2 | ABC | 2022-01-20 | 1 | 29.20 | 3
3 | ABC | 2022-01-20 | 1 | 29.20 | 3
4 | ABC | 2022-01-20 | 1 | 29.20 | 3
5 | ABC | 2022-01-20 | 1 | 29.20 | 3
6 | ABC | 2022-01-22 | 2 | 32.50 | 3
7 | ABC | 2022-01-22 | 2 | 32.50 | 3
8 | ABC | 2022-01-22 | 2 | 32.50 | 4
9 | XYZ | 2022-01-01 | 5 | 20.50 | 6
10 | XYZ | 2022-01-01 | 5 | 20.50 | 6
11 | XYZ | 2022-01-01 | 5 | 20.50 | 6
SELECT o.*
, CAST(i.AveragePriceIn AS DECIMAL(10,2)) AS cost_of_sales_uP
, i.TotalPriceIn AS cost_of_sales
FROM stock_transactions o
LEFT JOIN (
SELECT rowN_out
, AVG(unitPrice) as AveragePriceIn
, SUM(unitPrice) as TotalPriceIn
FROM #tmpStock
GROUP BY rowN_out
) i on i.rowN_out = o.rowN
WHERE o.sign_ = 'out'
ORDER BY o.rowN;
rowN | date_ | stockCode | sign_ | amount | unitPrice | cost_of_sales_uP | cost_of_sales
---: | :--------- | :-------- | :---- | -----: | --------: | ---------------: | ------------:
3 | 2022-01-23 | ABC | out | 7 | 40.00 | 30.14 | 211.00
4 | 2022-01-23 | ABC | out | 1 | 42.00 | 32.50 | 32.50
6 | 2022-01-03 | XYZ | out | 3 | 25.00 | 20.50 | 61.50
Demo on db<>fiddle here
I have a very simple requirement but I'm struggling to find a way around this.
I have a very simple query:
SELECT
ServiceCode,
StartDate,
Available,
Nights,
BookingID
FROM #tmpAvailability
LEFT JOIN vwRSBooking B
ON B.Depart = A.StartDate
AND B.ServiceCode = A.SupplierCode
AND B.StatusID IN (2640, 2621)
ORDER BY StartDate;
Made up of 2 tables
#tmpAvailability which consists of the following fields:
SupplierCode
StartDate
Available
vwRSBooking which consists of the following fields
BookingID
DepartDate
Code
Nights
StatusID
Departure and startdate can be joined to link the first day, and the servicecode and suppliercode can be joined to make sure that the availability is linked to the same supplier.
Which produces an output like this:
Code | Dates | Available | Nights | BookingID
TEST | 2018-01-04 | 1 | NULL | NULL
TEST | 2018-01-05 | 1 | NULL | NULL
TEST | 2018-01-06 | 0 | 4 | 123456
TEST | 2018-01-07 | 0 | NULL | NULL
TEST | 2018-01-08 | 0 | NULL | NULL
TEST | 2018-01-09 | 0 | NULL | NULL
TEST | 2018-01-10 | 1 | NULL | NULL
TEST | 2018-01-11 | 1 | NULL | NULL
TEST | 2018-01-12 | 1 | NULL | NULL
TEST | 2018-01-13 | 0 | NULL | 234567
TEST | 2018-01-14 | 0 | NULL | NULL
TEST | 2018-01-15 | 0 | NULL | NULL
What I need is when the BookingID in for 4 days that the bookingID and the nights are spread across those days, for example:
Code | Dates | Available | Nights | BookingID
TEST | 2018-01-04 | 1 | NULL | NULL
TEST | 2018-01-05 | 1 | NULL | NULL
TEST | 2018-01-06 | 0 | 4 | 123456
TEST | 2018-01-07 | 0 | 4 | 123456
TEST | 2018-01-08 | 0 | 4 | 123456
TEST | 2018-01-09 | 0 | 4 | 123456
TEST | 2018-01-10 | 1 | NULL | NULL
TEST | 2018-01-11 | 1 | NULL | NULL
TEST | 2018-01-12 | 1 | NULL | NULL
TEST | 2018-01-13 | 0 | 3 | 234567
TEST | 2018-01-14 | 0 | 3 | 234567
TEST | 2018-01-15 | 0 | 3 | 234567
TEST | 2018-01-16 | 1 | NULL | NULL
If anyone has any ideas on how to solve it would be most appreciated.
Andrew
You could replace your vwRSBooking with another view which uses a CTE to obtain all the dates the booking covers. Then use the view's coverdate for joining to the #tmpAvailability table:
CREATE VIEW vwRSBookingFull
AS
WITH cte ( bookingid, nights, depart, code, coverdate)
AS (SELECT bookingid,
nights,
depart,
code,
depart
FROM vwRSBooking
UNION ALL
SELECT c.bookingid,
c.nights,
c.depart,
c.code,
DATEADD(d, 1, c.coverdate)
FROM cte c
WHERE DATEDIFF(d, c.depart, c.coverdate) < (c.nights - 1))
SELECT c.bookingid,
c.nights,
c.depart,
c.code,
c.coverdate
FROM cte c
GO
You will need a calendar table with all the dates in the date range your dates may fall into. For this example, I build one for January 2018. We can then join onto this table to create the additional rows.
Here is the sample code I used. You can see it at SQL Fiddle.
CREATE TABLE code (
code varchar(max),
dates date,
available int,
nights int,
bookingid int
)
INSERT INTO code VALUES
('TEST','2018-01-04','1',NULL,NULL),
('TEST','2018-01-05','1',NULL,NULL),
('TEST','2018-01-06','0',4,123456),
('TEST','2018-01-07','0',NULL,NULL),
('TEST','2018-01-08','0',NULL,NULL),
('TEST','2018-01-09','0',NULL,NULL),
('TEST','2018-01-10','1',NULL,NULL),
('TEST','2018-01-11','1',NULL,NULL),
('TEST','2018-01-12','1',NULL,NULL),
('TEST','2018-01-13','0',3,234567),
('TEST','2018-01-14','0',NULL,NULL),
('TEST','2018-01-15','0',NULL,NULL)
CREATE TABLE dates (
dates date
)
INSERT INTO dates VALUES
('2018-01-01'),('2018-01-02'),('2018-01-03'),('2018-01-04'),('2018-01-05'),('2018-01-06'),('2018-01-07'),('2018-01-08'),('2018-01-09'),('2018-01-10'),('2018-01-11'),('2018-01-12'),('2018-01-13'),('2018-01-14'),('2018-01-15'),('2018-01-16'),('2018-01-17'),('2018-01-18'),('2018-01-19'),('2018-01-20'),('2018-01-21'),('2018-01-22'),('2018-01-23'),('2018-01-24'),('2018-01-25'),('2018-01-26'),('2018-01-27'),('2018-01-28'),('2018-01-29'),('2018-01-30'),('2018-01-31')
Here is the query based on this dataset:
SELECT
code.code,
dates.dates,
code.available,
code.nights,
code.bookingid
FROM code
LEFT JOIN dates ON
dates.dates >= code.dates
AND dates.dates < DATEADD(DAY,nights,code.dates)
Edit: Here is an example using your initial query as a subquery to join your result set onto the dates table if you want a copy & paste. Still requires creating the dates table.
SELECT
ServiceCode,
StartDate,
Available,
Nights,
BookingID
FROM (
SELECT
ServiceCode,
StartDate,
Available,
Nights,
BookingID
FROM #tmpAvailability
LEFT JOIN vwRSBooking B
ON B.Depart = A.StartDate
AND B.ServiceCode = A.SupplierCode
AND B.StatusID IN (2640, 2621)
) code
LEFT JOIN dates ON
dates.dates >= code.dates
AND dates.dates < DATEADD(DAY,nights,code.dates)
ORDER BY StartDate;
i need to do one select with different where clauses (with different product code). The result which i need is below.
I have table like:
ApID | Date | Code | Qty | Price | Sum
222 | 2014-10-11 | 555 | 1 | 2,22 | 2,22
222 | 2014-10-11 | 555 | 1 | 2,22 | 2,22
222 | 2014-10-11 | 333 | 1 | 3,33 | 3,33
222 | 2014-10-12 | 555 | 1 | 2,22 | 2,22
My query:
SELECT
CAST(Date AS DATE) as 'Data',
SUM(Qty*Price) AS 'Sum',
SUM(Qty) AS 'Qty'
FROM Table
WHERE ApID = 222
AND Data BETWEEN '2014-10-11' AND '2014-10-13'
AND Code LIKE '555'
GROUP BY CAST(KvitoGalva.Data AS DATE)
I get result like this:
Data | Sum | Qty
2014-10-11 | 4.44 | 2
2014-10-12 | 2.22 | 1
I need to display result in one table:
Data | Sum 555 | Qty 555 | Sum 333 | Qty 333 |
2014-10-11 | 4.44 | 2 | 3.33 | 1 |
2014-10-12 | 2.22 | 1 | 0 | 0 |
Tried:
SELECT((Select1),(Select2))
----------------------------
SELECT 1
union
select 2
You can do this using conditional aggregation:
SELECT CAST(Date AS DATE) as Data,
SUM(case when code = '555' then Qty*Price else 0 end) AS Sum555,
SUM(case when code = '555' then Qty else 0 end) AS Qty555,
SUM(case when code = '333' then Qty*Price else 0 end) AS Sum333,
SUM(case when code = '333' then Qty else 0 end) AS Qty333
FROM Table t
WHERE ApID = 222 AND
Data BETWEEN '2014-10-11' AND '2014-10-13' AND
Code IN ('555', '333')
GROUP BY CAST(KvitoGalva.Data AS DATE);
Note: only use single quotes for string and date constants. You do not need quotes for column aliases and if you did, then your database would have a better character for escaping the names.
I have a Customer table and an Address table.
The Address table has a flag which is either INVOICE, CORRESPONDENCE or DELIVERY.
A Customer can have 0 to many Address records.
I want to be able to query both tables and generate a flag for each customer based on the address data - no address records = NONE, 1 or more INVOICE records = HASINVOICE, no INVOICE but 1 or more others = HASOTHER
so, for the following data:
+------------+---------+
| CustomerID | Name |
+------------+---------+
| 1 | Peter |
| 2 | Ray |
| 3 | Egon |
| 4 | Winston |
| 5 | Dana |
+------------+---------+
+-----------+------------+----------------+
| AddressID | CustomerID | AddressType |
+-----------+------------+----------------+
| 1 | 1 | INVOICE |
| 2 | 1 | DELIVERY |
| 3 | 2 | DELIVERY |
| 4 | 2 | CORRESPONDENCE |
| 5 | 4 | INVOICE |
| 6 | 5 | CORRESPONDENCE |
+-----------+------------+----------------+
I would expect the following output:
+------------+---------+-------------+
| CustomerID | Name | AddressFlag |
+------------+---------+-------------+
| 1 | Peter | HASINVOICE |
| 2 | Ray | HASOTHER |
| 3 | Egon | NONE |
| 4 | Winston | HASINVOICE |
| 5 | Dana | HASOTHER |
+------------+---------+-------------+
Is this possible, for SQL 2000, using a single query and no cursors?
I don't have a 2000 instance handy (you really should upgrade, you're 4-5 releases behind), but I think that this should work:
declare #Customers table (CustomerID int,Name varchar(10))
insert into #Customers (CustomerID,Name)
select 1,'Peter' union all select 2,'Ray' union all
select 3,'Egon' union all select 4,'Winston' union all
select 5,'Dana'
declare #Addresses table (AddressID int, CustomerID int,
AddressType varchar(30))
insert into #Addresses (AddressID,CustomerID,AddressType)
select 1,1,'INVOICE' union all select 2,1,'DELIVERY' union all
select 3,2,'DELIVERY' union all select 4,2,'CORRESPONDENCE' union all
select 5,4,'INVOICE' union all select 6,5,'CORRESPONDENCE'
select
c.CustomerID,
c.Name,
CASE MAX(CASE
WHEN a.AddressType = 'Invoice' THEN 2
WHEN a.AddressType IS NOT NULL THEN 1
END
) WHEN 2 THEN 'HASINVOICE'
WHEN 1 THEN 'HASOTHER'
ELSE 'NONE'
END as AddressFlag
from
#Customers c
left join
#Addresses a
on
c.CustomerID = a.CustomerID
group by
c.CustomerID,
c.Name
Produces:
CustomerID Name AddressFlag
----------- ---------- -----------
5 Dana HASOTHER
3 Egon NONE
1 Peter HASINVOICE
2 Ray HASOTHER
4 Winston HASINVOICE
I have 2 tables: Income (InvoiceDate, TotalAmount) and Outcome (ExpenseDate, TotalAmount).
Suppose that I have data for each column as below:
Income:
| INVOICEDATE | TOTALAMOUNT |
|-------------|-------------|
| 2013-10-16 | 22000 |
| 2013-10-17 | 14400 |
| 2013-10-18 | 4488 |
Outcome:
| EXPENSEDATE | TOTALAMOUNT |
|-------------|-------------|
| 2013-10-25 | 15 |
I want to merge these 2 tables to show as below:
| DATE | INCOME | OUTCOME |
|------------|--------|---------|
| 2013-10-25 | 0 | 15 |
| 2013-10-16 | 22000 | 0 |
| 2013-10-17 | 14400 | 0 |
| 2013-10-18 | 4488 | 0 |
However when I run my T-SQL, It will show like this instead:
| DATE | INCOME | OUTCOME |
|------------|--------|---------|
| (null) | (null) | 15 |
| 2013-10-16 | 22000 | (null) |
| 2013-10-17 | 14400 | (null) |
| 2013-10-18 | 4488 | (null) |
This is my T-SQL:
SELECT
CASE (income.InvoiceDate)
WHEN NULL THEN Outcome.expenseDate
ELSE income.InvoiceDate
END AS [Date],
CASE (income.TotalAmount)
WHEN NULL THEN 0
ELSE income.TotalAmount
END AS Income,
CASE (Outcome.TotalAmount)
WHEN NULL THEN 0
ELSE Outcome.TotalAmount
END AS Outcome
FROM
Outcome
FULL OUTER JOIN
income ON Outcome.expenseDate = income.InvoiceDate
WHERE
NOT (
Outcome.TotalAmount = 0
AND income.TotalAmount = 0
)
You can test this SQL at http://sqlfiddle.com/#!6/3589f/1
Does anyone know what's wrong with my T-SQL?
Thank You!
Pengan
A CASE statement is a shorthand for a series of = operators. However, NULL is never equal to any value (that's what the IS operator is for), so using CASE to evaluate NULLs is somewhat pointless.
Instead, you can yse the COALESCE function to replace your NULLs with 0s as following:
SELECT Outcome.expenseDate AS [Date],
COALESCE(Income.TotalAMount, 0) AS Income,
COALESCE(Outcome.TotalAMount, 0) AS Outcome
FROM Outcome
FULL OUTER JOIN income ON Outcome.expenseDate = income.InvoiceDate
WHERE NOT (
Outcome.TotalAmount = 0
AND income.TotalAmount = 0
)
You may want a union instead
select InvoiceDate as Date, TotalAmount as Income, 0 as Outcome from Income
union all
select ExpenseDate, 0, TotalAmount from Outcome
If you can have income and expenses on the same date, you can group them from this.
select [date], Sum(Income), Sum(outcome) from
(
select InvoiceDate as Date, TotalAmount as Income, 0 as Outcome from Income
union all
select ExpenseDate, 0, TotalAmount from Outcome
) v
group by [date]
As to what is wrong with your statement, the problem is with the case.
They should use is null rather than when null
CASE WHEN income.TotalAmount IS NULL THEN 0 ELSE income.TotalAmount end,
But a shorter way is to use ISNULL
ISNULL(income.TotalAmount, 0)