I've got 2 tables (shown below) - Invoice and Payments. A single invoice can have multiple payments. I need to SUM the total of PayAmt from Payments and update the PayTotal column in the Invoice table.
This is NOT working:
UPDATE Invoices
SET Invoices.PayTotal = Payments.Total
FROM Invoices
INNER JOIN
(SELECT InvNum_FK, SUM(PayAmt) as Total
FROM Payments) ON Invoices.InvNum_PK = Payments.InvNum_FK
CREATE TABLE dbo.Invoices
(
InvNum_PK nvarchar(255) PRIMARY KEY,
InvAmt money,
InvDate date,
CustName nvarchar(255),
PayTotal money,
PayCount int
);
CREATE TABLE dbo.Payments
(
PayNum_PK int PRIMARY KEY,
InvNum_FK nvarchar(255)
FOREIGN KEY REFERENCES dbo.Invoices(InvNum_PK),
PayAmt money,
PayDate date,
);
INSERT INTO Invoices
VALUES ('GLI101', 838.93, '2021-08-01', 'George Washington', 0, 0);
INSERT INTO Invoices
VALUES ('GLI202', 1280.26, '2021-08-02', 'Abe Lincoln', 0, 0);
INSERT INTO Invoices
VALUES ('GLI303', 1456.23, '2021-08-03', 'Tom Jefferson', 0, 0);
INSERT INTO Invoices
VALUES ('GLI404', 1124.97, '2021-08-04', 'Jim Madison', 0, 0);
INSERT INTO Payments VALUES (1, 'GLI101', 223.33, '08/15/2021')
INSERT INTO Payments VALUES (2, 'GLI101', 211.88, '09/16/2021')
INSERT INTO Payments VALUES (3, 'GLI101', 316.44, '09/14/2021')
INSERT INTO Payments VALUES (4, 'GLI404', 415.46, '09/10/2021')
INSERT INTO Payments VALUES (5, 'GLI404', 115.46, '09/04/2021')
This works if anyone looks at this post in the future with the same question:
WITH cte AS (
SELECT InvNum_FK, SUM(PayAmt) AS PayTotal
FROM Payments
GROUP BY InvNum_FK)
UPDATE Invoices SET Invoices.PayTotal = cte.PayTotal
FROM Invoices INNER JOIN cte ON Invoices.InvNum_PK = cte.InvNum_FK
I am stuck with the following queries. Anyone can help?
Show the full name and the phone number of the customer who made the most orders.
Which item is the best seller? Display description and price.
Attached is my ERD.
create table statements are as follows:
CREATE TABLE ITEMS (
itemID number(5),
itemDescription varchar2(60),
itemSize varchar2(22),
itemColour varchar2(10),
itemPrice number (5,2),
itemQuantityAvailable number(3),
CONSTRAINT pk_items PRIMARY KEY (itemID));
CREATE TABLE CUSTOMERS (
custID number(5),
custLName varchar2(10),
custFName varchar2(20),
custAddress varchar2(30),
custTown varchar2(20),
custPostcode number(4),
custPhone number(10),
custEmail varchar2(30),
shopID number(5),
CONSTRAINT pk_customers PRIMARY KEY (custID),
CONSTRAINT fk_customers_shopID FOREIGN KEY (shopID)
REFERENCES SHOPS (shopID));
CREATE TABLE ORDERS (
orderNo number(5),
orderDate Date,
dispatchDate date,
custID number(5),
CONSTRAINT pk_orders PRIMARY KEY (orderNo),
CONSTRAINT fk_orders_custID FOREIGN KEY (custID)
REFERENCES CUSTOMERS (custID));
CREATE TABLE OrderQuantity (
orderNo number(5),
itemID number(5),
orderQuantity number(3),
CONSTRAINT pk_orderQty PRIMARY KEY (itemID, orderNo),
CONSTRAINT fk_orderQty_itemID FOREIGN KEY (itemID)
REFERENCES ITEMS (itemID),
CONSTRAINT fk_orderQty_orderNo FOREIGN KEY (orderNO)
REFERENCES ORDERS (orderNo));
INSERT STATEMENTS ARE AS FOLLOWS:
INSERT INTO ITEMS VALUES (1,'ADIDAS MEN''S ID STADIUM FULL-ZIP JACKET','S/M/L/XL','Silver',89.99,100);
INSERT INTO ITEMS VALUES (2,'ADIDAS MEN''S ESSENTIALS 3 STRIPES CREW FRENCH TERRY','S/L/XL','Black',71.99,90);
INSERT INTO ITEMS VALUES (3,'ADIDAS MEN''s ESSENTIALS 3-STRIPES FLEECE PANT','M/L/XL/XXL','White',62.99,80);
INSERT INTO ITEMS VALUES (4,'NIKE MEN''S FULL-ZIP SPORTSWEAR HOODIE','M/L/XL','DarkGrey',67.49,70);
INSERT INTO ITEMS VALUES (5,'NIKE MEN''S HBR FLEECE CREW','S/L/XL','DarkGrey',58.49,60);
INSERT INTO ITEMS VALUES (6,'PUMA MEN''S ESSENTIAL SWEAT PANTS','XS/S/M/L/XL','Navy',45.00,50);
INSERT INTO ITEMS VALUES (7,'ADIDAS MEN''S ESSENTIALS CHELSEA SHORTS','M/L/XL','Green',35.99,40);
INSERT INTO ITEMS VALUES (8,'PUMA MEN''S CORE 7IN RUNNING SHORTS','S/L/XL','Black',26.99,20);
INSERT INTO ITEMS VALUES (9,'ASICS GT 2000 6 MEN''S RUNNING SHOES','8/8.5/9/9.5/10','Silver',239.00,90);
INSERT INTO ITEMS VALUES (10,'ASICS GEL-KAYANO 25 2E WIDE MEN''S RUNNING SHOE','9/9.5/10/10.5/11','Black',219.00,60);
INSERT INTO ITEMS VALUES (11,'REEBOK RUNNER 3.0 MEN''S RUNNING SHOES','8/8.5/10/10.5','Orange',209.00,10);
INSERT INTO ITEMS VALUES (12,'REEBOK MEN''S WORKOUT READY 2.0 TEE','XS/S/M','White',199.00,50);
INSERT INTO ITEMS VALUES (13,'UNDER ARMOUR RAPID MEN''S RUNNING SHOES','8.5/10/11','Black',199.00,20);
INSERT INTO ITEMS VALUES (14,'UNDER ARMOUR CHARGED MEN''S RUNNING SHOES','8/8.5/9/9.5/10','Silver',159.00,80);
INSERT INTO ITEMS VALUES (15,'ADIDAS SOLAR DRIVE MEN''S RUNNING SHOE','8/8.5/10/10.5','Black',139.00,30);
INSERT INTO CUSTOMERS VALUES (1,'Widimer','Cindy','121 King St','Box Hill',3195,0470121566,'cindy_widimer#gmail.com',1);
INSERT INTO CUSTOMERS VALUES (2,'Bohrman','Robert','54 Queens St','Murrumbeena',3195,0455131565,'robert_bohram#gmail.com',3);
INSERT INTO CUSTOMERS VALUES (3,'Clarke','Brenda','89 Pecket St','Oakleigh',3195,0424456123,'brenda.clarke#gmail.com',2);
INSERT INTO CUSTOMERS VALUES (4,'Cartier','Bruce','12 Parkore St','Mulgrave',3195,0425654123,'bruce.cartier#gmail.com',3);
INSERT INTO CUSTOMERS VALUES (5,'Heart','Lucy','45 Rose St','Hampton Park',3178,0470456321,'lucy.heart#gmail.com',1);
INSERT INTO CUSTOMERS VALUES (6,'Carter','Joan','123 Davdison St','Hampton Park',3178,0452123789,'carter_joangmail.com',3);
INSERT INTO CUSTOMERS VALUES (7,'John','Ailene','56 Spring St','Yaraman',3178,0475123159,'john.ailene#gmail.com',1);
INSERT INTO CUSTOMERS VALUES (8,'Lewis','Carl','45 Brendon Ct','Noble Park',3178,0454789456,'carl_lewis#gmail.com',3);
INSERT INTO CUSTOMERS VALUES (9,'Holden','Hilary','56 Swanston St','Sunshine',3020,0421456123,'hilar_holdengmail.com',1);
INSERT INTO CUSTOMERS VALUES (10,'Powell','Venus','124 Russel St','Richmond',3112,0431456753,'venus_powellgmail.com',2);
INSERT INTO CUSTOMERS VALUES (11,'Juliet','Susan','87 Fawkner Ct','South Yara',3141,0424687423,'susan_juliet#gmail.com',2);
INSERT INTO CUSTOMERS VALUES (12,'Price','Victor','125 William St','Flagstaff',3003,0465789123,'victor_price#gmail.com',2);
INSERT INTO ORDERS VALUES (1,'01/Jan/18','03/Feb/18',1);
INSERT INTO ORDERS VALUES (2,'20/Jan/18','05/Feb/18',3);
INSERT INTO ORDERS VALUES (3,'05/Feb/18','10/Feb/18',4);
INSERT INTO ORDERS VALUES (4,'18/Feb/18','22/Feb/18',5);
INSERT INTO ORDERS VALUES (5,'01/Mar/18','10/Mar/18',10);
INSERT INTO ORDERS VALUES (6,'12/Mar/18','15/Mar/18',12);
INSERT INTO ORDERS VALUES (7,'07/Apr/18','14/Apr/18',11);
INSERT INTO ORDERS VALUES (8,'25/Apr/18','27/Apr/18',3);
INSERT INTO ORDERS VALUES (9,'06/May/18','10/May/18',2);
INSERT INTO ORDERS VALUES (10,'17/Jun/18','20/Jun/18',5);
INSERT INTO ORDERS VALUES (11,'29/Jun/18','07/Jul/18',4);
INSERT INTO ORDERS VALUES (12,'08/Jul/18','14/Jul/18',11);
INSERT INTO ORDERS VALUES (13,'25/Jul/18','29/Jul/18',12);
INSERT INTO ORDERS VALUES (14,'01/Aug/18','18/Aug/18',5);
INSERT INTO ORDERS VALUES (15,'19/Aug/18','10/Sep/18',1);
INSERT INTO ORDERS VALUES (16,'11/Sep/18','15/Sep/18',4);
INSERT INTO ORDERS VALUES (17,'21/Sep/18','29/Sep/18',5);
INSERT INTO ORDERS VALUES (18,'01/Oct/18',null,10);
INSERT INTO ORDERS VALUES (19,'20/Oct/18',null,3);
INSERT INTO ORDERS VALUES (20,'23/Oct/18',null,1);
INSERT INTO OrderQuantity VALUES (1,1,20);
INSERT INTO OrderQuantity VALUES (2,3,10);
INSERT INTO OrderQuantity VALUES (3,4,20);
INSERT INTO OrderQuantity VALUES (4,5,14);
INSERT INTO OrderQuantity VALUES (5,10,10);
INSERT INTO OrderQuantity VALUES (6,11,15);
INSERT INTO OrderQuantity VALUES (7,12,10);
INSERT INTO OrderQuantity VALUES (8,3,25);
INSERT INTO OrderQuantity VALUES (9,2,50);
INSERT INTO OrderQuantity VALUES (10,5,20);
INSERT INTO OrderQuantity VALUES (11,4,10);
INSERT INTO OrderQuantity VALUES (12,11,20);
INSERT INTO OrderQuantity VALUES (13,12,10);
INSERT INTO OrderQuantity VALUES (14,5,10);
INSERT INTO OrderQuantity VALUES (15,1,10);
INSERT INTO OrderQuantity VALUES (16,4,30);
INSERT INTO OrderQuantity VALUES (17,5,9);
INSERT INTO OrderQuantity VALUES (18,10,30);
INSERT INTO OrderQuantity VALUES (19,3,20);
INSERT INTO OrderQuantity VALUES (20,1,15);
This is a simple example of how to solve it by making a subquery and only select the first row from it.
SELECT full_name, custPhone
FROM (SELECT CONCAT(custFName, ' ', custLName) as full_name, custPhone, COUNT(o.orderNo) num_orders
FROM ORDERS o
JOIN Customers c ON c.custId = o.custid
GROUP BY full_name, custPhone
ORDER BY num_orders Desc ) AS subq
WHERE ROWNUM = 1
and the same for the second query
SELECT itemDescription, itemPrice
FROM (SELECT itemDescription, itemPrice, SUM(orderQuantity) sum_quantity
FROM OrderQuantity o
JOIN Items i ON i.itemId = o.itemId
GROUP BY itemDescription, itemPrice
ORDER BY sum_quantity DESC) AS subq
WHERE ROWNUM = 1
check this sqlfiddle http://sqlfiddle.com/#!5/553399/39/0
The Full name and the phone number of the customers who made the most orders :
SELECT ORDERS.custID,CUSTOMERS.custFName,CUSTOMERS.custPhone,COUNT(ORDERS.orderNo )
FROM ORDERS inner join CUSTOMERS
on CUSTOMERS.custID =ORDERS.custID
group by ORDERS.custID,CUSTOMERS.custFName,CUSTOMERS.custPhone
order by COUNT(ORDERS.orderNo ) desc
Description and price of Bestseller Item (qty bestseller):
SELECT OrderQuantity.itemID ,ITEMS.itemDescription,ITEMS.itemPrice ,sum(distinct OrderQuantity.itemID )
FROM OrderQuantity inner join ITEMS
on OrderQuantity.itemID =ITEMS.itemID
GROUP BY OrderQuantity.itemID ,ITEMS.itemDescription,ITEMS.itemPrice
ORDER BY sum(distinct OrderQuantity.itemID) DESC
if you need only top bestseller item you can add rownum 1 to these queries.
select * from (SELECT OrderQuantity.itemID ,ITEMS.itemDescription,ITEMS.itemPrice ,sum(distinct OrderQuantity.itemID )
FROM OrderQuantity inner join ITEMS
on OrderQuantity.itemID =ITEMS.itemID
GROUP BY OrderQuantity.itemID ,ITEMS.itemDescription,ITEMS.itemPrice
ORDER BY sum(distinct OrderQuantity.itemID) DESC) q
Where rownum=1
I have a table (source) with payments for a person - called 'Item' in the example below.
This table will have payments for each person, added to it over a period.
I then generate invoices, which basically takes all the payments for a particular person, and sums them up into a single row.
This must be stored in an invoice table, for auditing reasons.
I do this in the example below.
What I am missing, though, as I am not sure how to do it, is that each payment, once assigned to the Invoice table, needs to had the Invoice ID that it was assigned to, stored in the Items table.
So, see the example below:
CREATE TABLE Items
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
PaymentValue DECIMAL(16,2) NOT NULL,
AssignedToInvoiceID INT NULL
)
CREATE TABLE Invoice
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
Value DECIMAL(16,2)
)
INSERT INTO Items (PersonID, PaymentValue) VALUES (1, 100)
INSERT INTO Items (PersonID, PaymentValue) VALUES (2, 132)
INSERT INTO Items (PersonID, PaymentValue) VALUES (2, 65)
INSERT INTO Items (PersonID, PaymentValue) VALUES (1, 25)
INSERT INTO Items (PersonID, PaymentValue) VALUES (3, 69)
SELECT * FROM Items
INSERT INTO Invoice (PersonID, Value)
SELECT PersonID, SUM(PaymentValue) FROM Items
WHERE AssignedToInvoiceID IS NULL
GROUP BY PersonID
SELECT * FROM Invoice
DROP TABLE Items
DROP TABLE Invoice
What I need to do is then update the Items table, to say that the first row has been assigned to Invoice.ID 1, row two was assigned to Invoice ID 2. Row 3, was assigned to Invoice ID 2 as well.
Note, there are many other columns in the table. This is a basic example.
Simply, I need to record which invoice, each source row was assigned to.
The key thing here to ensure payments are correctly linked to invoices is to ensure that:
A: No updates are made to Items between reading the unassigned items and updating AssignedToInvoiceID.
B: No new invoices are created with the Items being process before updating AssignedToInvoiceID.
As you are updating two tables it will have to be a two step process. To ensure A it will need to be run in a transaction with a least REPEATABLE READ isolation. To ensure B requires a transaction with SERIALIZABLE isolation. See SET TRANSACTION ISOLATION LEVEL
It can be done like this:
BEGIN TRAN
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
DECLARE #newInvoices TABLE (PersonID INT, InvoiceID INT)
INSERT INTO Invoice (PersonID, Value)
OUTPUT inserted.ID, inserted.PersonID INTO #newInvoices(InvoiceID, PersonID)
SELECT PersonID, SUM(PaymentValue) FROM Items
WHERE AssignedToInvoiceID IS NULL
GROUP BY PersonID
UPDATE Items
SET AssignedToInvoiceID = InvoiceID
FROM Items
INNER JOIN #newInvoices newInvoice ON newInvoice.PersonID = Items.PersonID
WHERE AssignedToInvoiceID IS NULL
COMMIT
An alternative if you are using SQL Server 2012 or later is to use the SEQUNCE object, this will allow the Items to be assigned new invoice IDs before the Invoices are created, reducing the locking required.
It works like this:
-- Run once with your table setup.
CREATE SEQUENCE InvoiceIDs AS INT START WITH 1 INCREMENT BY 1
CREATE TABLE Items
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
PaymentValue DECIMAL(16,2) NOT NULL,
AssignedToInvoiceID INT NULL
)
CREATE TABLE Invoice
(
-- No longer a IDENTITY column
ID INT NOT NULL,
PersonID INT NOT NULL,
Value DECIMAL(16,2)
)
BEGIN TRAN
DECLARE #newInvoiceLines TABLE (PersonID INT, InvoiceID INT, PaymentValue DECIMAL(16,2))
-- Reading and updating AssignedToInvoiceID happens in one query so is thread safe.
UPDATE Items
SET AssignedToInvoiceID = newInvoices.InvoiceID
OUTPUT inserted.PersonID, inserted.AssignedToInvoiceID, inserted.PaymentValue
INTO #newInvoiceLines(PersonID, InvoiceID, PaymentValue)
FROM Items
INNER JOIN (
SELECT PersonID, NEXT VALUE FOR InvoiceIDs AS InvoiceID
FROM Items
GROUP BY PersonID
) AS newInvoices ON newInvoices.PersonID = Items.PersonID
WHERE Items.AssignedToInvoiceID IS NULL
INSERT INTO Invoice (ID, PersonID, Value)
SELECT InvoiceID, PersonID, SUM(PaymentValue) FROM #newInvoiceLines
GROUP BY PersonID, InvoiceID
COMMIT
You will still want to use a transaction to ensure the Invoice gets created.
Based on what I understand, you could run an update from join after you have inserted records into Invoices table, like so:
update items
set assignedtoinvoiceid = v.id
from
items m
inner join invoice v on m.personid = v.personid
Demo
Each time you do an invoice "run", select the most recent invoice for each person something like,
update items
set AssignedToInvoiceID = inv.id
from
(select personid, max(id) id
from invoice
group by personid) inv
where items.personid = inv.personid
and AssignedToInvoiceID is null
this assumes that AssignedToInvoiceID is null when it isn't populated, if it gets defaulted to an empty string or something then you would need to change the where condition.
1) Get MAX(ID) from Invoice table before inserting new rows from Items table. Store this value into a variable: #MaxInvoiceID
2) After inserting records into Invoice table, update AssignedToInvoiceID in Items table with Invoice.ID>#MaxInvoiceID
Refer below code:
CREATE TABLE #Items
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
PaymentValue DECIMAL(16,2) NOT NULL,
AssignedToInvoiceID INT NULL
)
CREATE TABLE #Invoice
(
ID INT NOT NULL IDENTITY(1,1),
PersonID INT NOT NULL,
Value DECIMAL(16,2)
)
DECLARE #MaxInvoiceID INT;
SELECT #MaxInvoiceID=ISNULL(MAX(ID),0) FROM #Invoice
SELECT #MaxInvoiceID
INSERT INTO #Items (PersonID, PaymentValue) VALUES (1, 100)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (2, 132)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (2, 65)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (1, 25)
INSERT INTO #Items (PersonID, PaymentValue) VALUES (3, 69)
SELECT * FROM #Items
INSERT INTO #Invoice (PersonID, Value)
SELECT PersonID, SUM(PaymentValue)
FROM #Items
WHERE AssignedToInvoiceID IS NULL
GROUP BY PersonID
SELECT * FROM #Invoice
UPDATE Itm
SET Itm.AssignedToInvoiceID=Inv.ID
FROM #Items Itm
JOIN #Invoice Inv ON Itm.PersonID=Inv.PersonID AND Itm.AssignedToInvoiceID IS NULL AND Inv.ID>#MaxInvoiceID
SELECT * FROM #Items
DROP TABLE #Items
DROP TABLE #Invoice
I have a MSSQL Server table like this:
id (auto-increment)
amount
date
account_id
Data are inserted throughout the day. I now need a view to get the opening and closing amounts for each account for each day.
My trouble is creating a fast query to access both minimum and maximum values.
Creating a view to access just the minimum is fine using an in statement, however getting both minimum and maximum is tricky. I've tried using a with clause, but the query is incredibly slow.
BTW I am mapping the view to hibernate, so stored procedures and functions won't work the same way (that I know of).
Update
I guess my question wasn't clear from the responses I received. I want to get the opening and closing balances for each account. Maximum and minimum referred to getting the max and min (id) when grouped by date and account_id.
I want to get the amount when the id is equal to the maximum id (closing balance) and the amount when the id is equal to the minimum id (opening balance) for each account for each day.
SELECT account_id, date, MIN(amount), MAX(amount)
FROM <table>
GROUP BY account_id, date
There must be something missing from your question.
This does the work, don't have enough data to evaluate performance:
create table #accounts
(
id integer identity,
account_id integer,
amount decimal(18,3),
tran_date datetime
)
go
insert into #accounts values (1,124.56,'06/01/2009 09:34:56');
insert into #accounts values (1,125.56,'06/01/2009 10:34:56');
insert into #accounts values (1,126.56,'06/01/2009 11:34:56');
insert into #accounts values (2,124.56,'06/01/2009 09:34:56');
insert into #accounts values (2,125.56,'06/01/2009 10:34:56');
insert into #accounts values (2,126.56,'06/01/2009 11:34:56');
insert into #accounts values (3,124.56,'06/01/2009 09:34:56');
insert into #accounts values (3,125.56,'06/01/2009 10:34:56');
insert into #accounts values (3,126.56,'06/01/2009 11:34:56');
insert into #accounts values (4,124.56,'06/01/2009 09:34:56');
insert into #accounts values (4,125.56,'06/01/2009 10:34:56');
insert into #accounts values (4,126.56,'06/01/2009 11:34:56');
insert into #accounts values (1,124.56,'06/02/2009 09:34:56');
insert into #accounts values (1,125.56,'06/02/2009 10:34:56');
insert into #accounts values (1,126.56,'06/02/2009 11:34:56');
insert into #accounts values (2,124.56,'06/02/2009 09:34:56');
insert into #accounts values (2,125.56,'06/02/2009 10:34:56');
insert into #accounts values (2,126.56,'06/02/2009 11:34:56');
insert into #accounts values (3,124.56,'06/02/2009 09:34:56');
insert into #accounts values (3,125.56,'06/02/2009 10:34:56');
insert into #accounts values (3,126.56,'06/02/2009 11:34:56');
insert into #accounts values (4,124.56,'06/02/2009 09:34:56');
insert into #accounts values (4,125.56,'06/02/2009 10:34:56');
insert into #accounts values (4,126.56,'06/02/2009 11:34:56');
go
select
ranges.tran_day transaction_day,
ranges.account_id account_id,
bod.amount bod_bal,
eod.amount eod_bal
from
-- Subquery to define min/max records per account per day
(
select
account_id,
cast(convert(varchar(10),tran_date,101) as datetime) tran_day,
max(id) max_id,
min(id) min_id
from
#accounts
group by
account_id,
cast(convert(varchar(10),tran_date,101) as datetime)
) ranges
-- Beginning of day balance
JOIN #accounts bod
on (bod.id = ranges.min_id)
-- End of day balance
JOIN #accounts eod
on (eod.id = ranges.max_id)
go
If you need better performance, store the subquery to a temp table first and put an index on it for the joins ... that might speed it up a bit.
Based on John Saunders answer and Jeremy comment:
SELECT account_id, date, MIN(amount), MAX(amount)
FROM <table>
GROUP BY account_id, DatePart( Year, date ),DatePart( Month, date ), DatePart( Day, date )
Essentially I need the following query, but the with statement causes it to run slowly:
with x as (
select
MAX(ab.id) as maxId, MIN(ab.id) as minId
from Balance ab
group by ab.account_id, dbo.Get_PeriodDateFromDatetime(ab.StatementDate)
)
select
ab.Amount as openingBalance, ab2.Amount as closingBalance
from Balance ab, Balance ab2, x
where ab.id = x.maxId and ab2.id = x.minId
I don't know if this improves any, but the query you posted looks to be missing some parts, like account_id in the "with" query and joins on account_id in the main part:
with x as (
select
ab.account_id, MAX(ab.id) as closeId, MIN(ab.id) as openId
from Balance ab
group by ab.account_id, dbo.Get_PeriodDateFromDatetime(ab.StatementDate)
)
select
opbal.account_id, opbal.StatementDate,
opbal.Amount as openingBalance, clsbal.Amount as closingBalance
from Balance opbal, Balance clbal, x
where clsbal.id = x.closeId
and clsbal.ccount_id = x.account_id
and opbal.id = x.openId
and op.account_id = x.account_id
I'm a little concerned about the call to dbo.Get_PeriodDateFromDatetime(ab.StatementDate): if you have an index on account_id and StatementDate (you do have that index, don't you? It looks like a good candidate for a clustered index, too) then it's maybe not too bad, unless the table is massive.
How slow is "slow", by the way?