Update table from same table - sql-server

I have a data set in which I would like to update a column PREVACCEPTID.
The update is based on the contents of the same table, a sample data is shown below:
The column should be updated after a search to see if station has had previous acceptances and what was this?
If we SELECT all DISTINCT 'ACCEPTID' for station A we would get the below.
I want to use this DISTINCT ACCEPTID to populate 'PREVACCEPTID'.
So whereever I have an entry with for e.g. '142692', I would lookup the sub-table and check if there are exists any previous ACCEPTID s, if that is the case populate with the previous one, in this case '142691' (see after results table as they are populated)
I have tried a few things now, I am getting an error for the below:
UPDATE a
SET a.PREVACCEPTID = (CASE
WHEN COUNT(DISTINCT b.ACCEPTID) = 1
THEN b.ACCEPTID
WHEN COUNT(DISTINCT b.ACCEPTID) > 1
AND b.ACCEPTID <> MIN(a.ACCEPTID)
THEN b.ACCEPTID - 1
END)
FROM dbo.table a
RIGHT JOIN dbo.table b ON b.STATION = a.STATION
AND b.PERIOD = a.PERIOD
AND b.ACCEPTID = a.ACCEPTID
I get this error:
Msg 157, Level 15, State 1, Line 326
An aggregate may not appear in the set list of an UPDATE statement.
The end result is per below:
I think a cte would be better option, but i have never used one.
Thanks in advance.

If I interpret your question and subsequent comments correctly, I assume you want the previous AcceptID to be populated to be last AcceptID for a given set of rows sharing the same Station and Period. Last I assume would be defined by the time components (StackDate and QTime). And, in the case where there is only one row for a given Station and Period, you'd want the Previous AcceptID to be set to be the same as AcceptID for that row.
Under the above conditions, below is a query that will work. Note: Replace table 'Test' with your own table name.
UPDATE t SET PrevAcceptID =
ISNULL(
(SELECT TOP 1 AcceptID
FROM Test t2
WHERE t2.Station = t.Station AND t2.Period = t.Period AND t2.AcceptID < t.AcceptID ORDER BY StackDate DESC, QTime DESC),
AcceptID)
FROM Test AS t

Related

T-SQL Selecting TOP 1 In A Query With Aggregates/Groups

I'm still fairly new to SQL. This is a stripped down version of the query I'm trying to run. This query is suppose to find those customers with more than 3 cases and display either the top 1 case or all cases but still show the overall count of cases per customer in each row in addition to all the case numbers.
The TOP 1 subquery approach didn't work but is there another way to get the results I need? Hope that makes sense.
Here's the code:
SELECT t1.StoreID, t1.CustomerID, t2.LastName, t2.FirstName
,COUNT(t1.CaseNo) AS CasesCount
,(SELECT TOP 1 t1.CaseNo)
FROM MainDatabase t1
INNER JOIN CustomerDatabase t2
ON t1.StoreID = t2.StoreID
WHERE t1.SubmittedDate >= '01/01/2017' AND t1.SubmittedDate <= '05/31/2017'
GROUP BY t1.StoreID, t1.CustomerID, t2.LastName, t2.FirstName
HAVING COUNT (t1.CaseNo) >= 3
ORDER BY t1.StoreID, t1.PatronID
I would like it to look something like this, either one row with just the most recent case and detail or several rows showing all details of each case in addition to the store id, customer id, last name, first name, and case count.
Data Example
For these I usually like to make a temp table of aggregates:
DROP TABLE IF EXISTS #tmp;
CREATE TABLE #tmp (
CustomerlD int NOT NULL DEFAULT 0,
case_count int NOT NULL DEFAULT 0,
case_max int NOT NULL DEFAULT 0,
);
INSERT INTO #tmp
(CustomerlD, case_count, case_max)
SELECT CustomerlD, COUNT(tl.CaseNo), MAX(tl.CaseNo)
FROM MainDatabase
GROUP BY CustomerlD;
Then you can join this "tmp" table back to any other table you want to display the number of cases on, or the max case number on. And you can limit it to customers that have more than 3 cases with WHERE case_count > 3

How to compare records in same SQL Server table

My requirement is to compare each column of row with its previous row.
Compare row 2 with row 1
Compare row 3 with row 2
Also, if there is no difference, I need to make that column NULL. Eg: request_status_id of row 3 is same as that of row 2 so I need to update request_status_id of row 3 to NULL.
Is there a clean way to do this?
You can use the following UPDATE statement that employs LAG window function available from SQL Server 2012 onwards:
UPDATE #mytable
SET request_status_id = NULL
FROM #mytable AS m
INNER JOIN (
SELECT payment_history_id, request_status_id,
LAG(request_status_id) OVER(ORDER BY payment_history_id) AS prevRequest_status_id
FROM #mytable ) t
ON m.payment_history_id = t.payment_history_id
WHERE t.request_status_id = t.prevRequest_status_id
SQL Fiddle Demo here
EDIT:
It seems the requirement of the OP is to SET every column of the table
to NULL, in case the previous value is same as the current value. In this case the query becomes a bit more verbose. Here is an example with two columns being set. It can easily be expanded to incorporate any other column of the table:
UPDATE #mytable
SET request_status_id = CASE WHEN t.request_status_id = t.prevRequest_status_id THEN NULL
ELSE T.request_status_id
END,
request_entity_id = CASE WHEN t.request_entity_id = t.prevRequest_entity_id THEN NULL
ELSE t.request_entity_id
END
FROM #mytable AS m
INNER JOIN (
SELECT payment_history_id, request_status_id, request_entity_id,
LAG(request_status_id) OVER(ORDER BY payment_history_id) AS prevRequest_status_id,
LAG(request_entity_id) OVER(ORDER BY payment_history_id) AS prevRequest_entity_id
FROM #mytable ) t
ON m.payment_history_id = t.payment_history_id
SQL Fiddle Demo here

Get records according to a datetime difference between associated records with T-SQL

I have 2 tables
» Item
Id
CreatedOn
Type
...
» StatusChange
StatusId
StatusChangedOn
Status
ItemId
An item can have multiple statuschanges. I want to see how long it takes for the status to change from one specific status to another for a certain selection of items.
Specifically I want to know when statuschanges take more than 30 minutes.
What I did so far is this:
Create a temp table with items from the needed daterange
INSERT #tempSC2
SELECT ItemId FROM StatusChange sc2 WHERE sc2.Status = 'Requested' AND
sc2.CreatedOn >= '2015-01-12' AND sc2.CreatedOn < '2015-01-19'
Select all the statuschanges from these items, where the status equals the two statusses of which I need to calculate the difference in time and where the type of item is the wanted type.
INSERT #tempSC
SELECT sc.ItemId, sc.Status, sc.StatusChangedOn, i.Type
FROM StatusChange sc
INNER JOIN Item i ON sc.ItemId = i.ID
WHERE (i.Type = 'TypeX' OR i.Type = 'TypeY')
AND (sc.Status = 'Requested' OR sc.Status = 'Finished')
AND sc.ItemId IN (SELECT * FROM #tempSC2)
ORDER BY sc.ItemId
So now I have a list with only the needed statuschanges (one record for 'Requested' and one for 'Finished' with the same ItemId, and so on, its also possible there is a 'Requested' record but no 'Finished' record), but I need to get the difference in time between Requested and Finished for each ItemId and show all itemId's for which the difference in time betweens its statuschanges (only Requested and Finished statuschanges) is more than 20 mintues.
I'm not sure how to continue.. What could be my next step? I don't see any immediate possibilities using aggregates and group by.

Subquery returned more than 1 value. this is not permitted when the subquery follows = or when the subquery is used as an expression?

I want to update multiple tables and values after inserting values in one table so I created a trigger. It works fine for inserts of one row, but as soon I insert more rows, SQL Server gives me following error:
subquery returned more than 1 value. this is not permitted when the subquery follows = or when the subquery is used as an expression?
Here is my trigger:
CREATE TRIGGER [dbo].[tbl_Sales_ForInsert]
ON [dbo].[SALES]
FOR INSERT
AS
BEGIN
DECLARE #ITEMMODEL varchar(100)
SELECT #ITEMMODEL = ITEM_MODEL FROM inserted
UPDATE SALES
SET PROFIT = TOTAL_PRICE - (SELECT QUANTITY FROM SALES WHERE ITEM_MODEL = #ITEMMODEL) * (SELECT RATE FROM ITEM_DETAILS WHERE ITEM_MODEL = #ITEMMODEL)
WHERE ITEM_MODEL = #ITEMMODEL
UPDATE ITEM_DETAILS
SET QUANTITY = QUANTITY - (SELECT QUANTITY FROM SALES WHERE ITEM_MODEL = #ITEMMODEL)
WHERE ITEM_MODEL = #ITEMMODEL
--UPDATE ITEM_DETAILS SET AMOUNT = AMOUNT - (SELECT RATE FROM ITEM_DETAILS WHERE ITEM_MODEL=#ITEMMODEL) * (SELECT QUANTITY FROM SALES WHERE ITEM_MODEL=#ITEMMODEL) where ITEM_MODEL=#ITEMMODEL
END
As I insert data in SALES table for 1st time the update got successful but for 2nd time it gives me above error remember ITEM_MODEL is foreign key constraint in SALES table.
I have been suffering with this error can anyone help me please?
Your fundamental flaw is that you seem to expect the trigger to be fired once per row - this is NOT the case in SQL Server. Instead, the trigger fires once per statement, and the pseudo table Inserted might contain multiple rows.
Given that that table might contain multiple rows - which one do you expect will be selected here??
SELECT #ITEMMODEL = ITEM_MODEL FROM inserted
It's undefined - you might get the values from arbitrary rows in Inserted.
You need to rewrite your entire trigger with the knowledge the Inserted WILL contain multiple rows! You need to work with set-based operations - don't expect just a single row in Inserted!
So in your case, your trigger code should look something like this:
CREATE TRIGGER [dbo].[tbl_Sales_ForInsert]
ON [dbo].[SALES]
FOR INSERT
AS
BEGIN
-- update the dbo.Sales table, set "PROFIT" to the difference of
-- TOTAL_PRICE and (QUANTITY * RATE) from the "Inserted" pseudo table
UPDATE s
SET s.PROFIT = i.TOTAL_PRICE - (i.QUANTITY * i.RATE)
FROM dbo.Sales s
INNER JOIN Inserted i ON i.ITEM_MODEL = s.ITEM_MODEL
-- update the dbo.ITEM_DETAILS table
UPDATE id
SET id.QUANTITY = id.QUANTITY - i.Quantity
FROM dbo.ITEM_DETAILS id
INNER JOIN Inserted i ON id.ITEM_MODEL = i.ITEM_MODEL
END
Marc_s is right about expecting the inserted pseudo table containing more than one row. There are instances that a query might work if a subquery was just limited to one row with a TOP(1).
UPDATE SALES
SET PROFIT = TOTAL_PRICE - (SELECT TOP(1) QUANTITY FROM SALES WHERE ITEM_MODEL = #ITEMMODEL)
* (SELECT TOP(1) RATE FROM ITEM_DETAILS WHERE ITEM_MODEL = #ITEMMODEL)
WHERE ITEM_MODEL = #ITEMMODEL

JOIN ON subselect returns what I want, but surrounding select is missing records when subselect returns NULL

I have a table where I am storing records with a Created_On date and a Last_Updated_On date. Each new record will be written with a Created_On, and each subsequent update writes a new row with the same Created_On, but an updated Last_Updated_On.
I am trying to design a query to return the newest row of each. What I have looks something like this:
SELECT
t1.[id] as id,
t1.[Store_Number] as storeNumber,
t1.[Date_Of_Inventory] as dateOfInventory,
t1.[Created_On] as createdOn,
t1.[Last_Updated_On] as lastUpdatedOn
FROM [UserData].[dbo].[StoreResponses] t1
JOIN (
SELECT
[Store_Number],
[Date_Of_Inventory],
MAX([Created_On]) co,
MAX([Last_Updated_On]) luo
FROM [UserData].[dbo].[StoreResponses]
GROUP BY [Store_Number],[Date_Of_Inventory]) t2
ON
t1.[Store_Number] = t2.[Store_Number]
AND t1.[Created_On] = t2.co
AND t1.[Last_Updated_On] = t2.luo
AND t1.[Date_Of_Inventory] = t2.[Date_Of_Inventory]
WHERE t1.[Store_Number] = 123
ORDER BY t1.[Created_On] ASC
The subselect works fine...I see X number of rows, grouped by Store_Number and Date_Of_Inventory, some of which have luo (Last_Updated_On) values of NULL. However, those rows in the sub-select where luo is null do not appear in the overall results. In other words, where I get 6 results in the sub-select, I only get 2 in the overall results, and its only those rows where the Last_Updated_On is not NULL.
So, as a test, I wrote the following:
SELECT 1 WHERE NULL = NULL
And got no results, but, when I run:
SELECT 1 WHERE 1 = 1
I get back a result of 1. Its as if SQL Server is not relating NULL to NULL.
How can I fix this? Why wouldn't two fields compare when both values are NULL?
You could use Coalesce (example assuming Store_Number is an integer)
ON
Coalesce(t1.[Store_Number],0) = Coalesce(t2.[Store_Number],0)
The ANSI Null comparison is not enabled by default; NULL doesn't equal NULL.
You can enable this (if your business case and your Database design usage of NULL requires this) by the Hint:
SET ansi_nulls off
Another alternative basic turn around using:
ON ((t1.[Store_Number] = t2.[Store_Number]) OR
(t1.[Store_Number] IS NULL AND t2.[Store_Number] IS NULL))
Executing your POC:
SET ansi_nulls off
SELECT 1 WHERE NULL = NULL
Returns:
1
This also works:
AND EXISTS (SELECT t1.Store_Number INTERSECT SELECT t2.Store_Number)

Resources