SQL Server - Update table1 with max condition in table1 - sql-server

I'm fairly new to SQL. This site has been a great resource and I found answers to many questions so far. Now I'm a little stuck, so it's time for my first question.
I'm working in SQL Server 2012. How do I update FLAG = 'Y' when START_DATE = (SELECT CONTRACT, MIN(START_DATE) FROM #CONTR GROUP BY CONTRACT) ?
CREATE TABLE #CONTR
(
CONTRACT INT , -- PRIMARY KEY COL1
CONTRACT_LINE INT , -- PRIMARY KEY COL2
START_DATE INT , -- 0 = CURRENT MONTH, -3 = THREE MONTHS IN PAST
FLAG VARCHAR(1) -- 'Y' OR 'N', DEFAULTED TO 'N' WHEN TABLE POPULATED
)
This doesn't seem like it should be that difficult, but I just can't seem to get it to work.

UPDATE #CONTR
SET FLAG = 'Y'
FROM #CONTR C1
JOIN
(SELECT CONTRACT, MIN(START_DATE) AS START_DATE FROM #CONTR GROUP BY CONTRACT) C2
ON C1.START_DATE = C2.START_DATE

INSERT INTO #CONTR VALUES (1,1,100,'N')
INSERT INTO #CONTR VALUES (1,1,200,'N')
INSERT INTO #CONTR VALUES (2,1,100,'N')
INSERT INTO #CONTR VALUES (2,1,200,'N')
UPDATE
c
SET
c.FLAG = 'Y'
FROM
#CONTR c
JOIN (
SELECT
CONTRACT _CONTRACT ,
MAX(START_DATE) _START_DATE
FROM
#CONTR
GROUP BY
CONTRACT
) mc ON c.CONTRACT = mc.[_CONTRACT] AND c.START_DATE = mc.[_START_DATE]
Another way to do that. It is absolutely the same, just the syntax is different:
UPDATE
c
SET
c.FLAG = 'Y'
FROM
#CONTR c
CROSS APPLY (
SELECT
c1.CONTRACT _CONTRACT
FROM
#CONTR c1
WHERE c1.CONTRACT = c.CONTRACT
GROUP BY
CONTRACT
HAVING MAX(c1.START_DATE) = c.START_DATE
) mc;
And the results:
SELECT * FROM #CONTR
CONTRACT CONTRACT_LINE START_DATE FLAG
1 1 100 N
1 1 200 Y
2 1 100 N
2 1 200 Y

Related

Compare row with other rows in the same table in sql server

I have the below records in my table,
If the HoleNumber combination is not having 'A' and 'B' for the particular datetime, we need to remove the alphabets from the number.
i.e., Remove 'A' from third record and sixth record. Because, it doesn't have B combinations for that datetime.
delete from myTable
where id in
(
select id from myTable t1
inner join
(
select [date], left([holeNumber], len(holeNumber)-1) as hNumber
from myTable
group by [date], left([holeNumber], len(holeNumber)-1)
having count(holeNumber) = 1
) tmp
on t1.[date] = tmp.[date] and left(t1.holeNumber, len(holeNumber)-1) = tmp.hNumber);
would do it, provided your requirements are strictly to remove having only 1 type of holeNumber.
DBFiddle demo

Retrieve CDC net changes without primary key but with unique field

I was wondering if it is possible to retrieve the net changes similar to cdc.fn_cdc_get_net_changes_<capture_instance>(from_lsn , to_lsn, 'all with mask') of tables that don't have a primary key but do have a constraint that ensures that one (or more) column(s) is unique.
It took me a while but I think I have a solution that works, let me know if there's a better solution or if you see a bug in mine.
Let's assume a capture instance named capture_instance of a table with unique column ID and non-unique columns field1, field2 and field3 and variables #from_lsn and #to_lsn.
WITH
cdc_all AS (
-- Retrieve the change table with all changes
SELECT *
FROM cdc.fn_cdc_get_all_changes_capture_instance(#from_lsn, #to_lsn, 'all')
),
f AS (
SELECT cdc_all.*, ops.[delete], ops.[insert], ops.[update], ops.[net_op]
FROM cdc_all
INNER JOIN (
-- Retrieve three flags for insert, update and delete and the net operation
-- also filter insert + delete pairs because it results in no change
SELECT *
FROM (
SELECT ID
, MAX(CASE WHEN __$operation = 1 THEN 1 ELSE 0 END) as [delete]
, MAX(CASE WHEN __$operation = 2 THEN 1 ELSE 0 END) as [insert]
, MAX(CASE WHEN __$operation = 4 THEN 1 ELSE 0 END) as [update]
, MIN(__$operation) [net_op]
FROM cdc_all
GROUP BY ID
) ops
WHERE NOT (ops.[delete] = 1 AND ops.[insert] = 1)
) ops ON cdc_all.ID = ops.ID
)
SELECT net.[max_lsn], f.[net_op] __$operation
, (CASE WHEN net.__$update_mask != 0x0 THEN net.__$update_mask ELSE NULL END) __$update_mask
, f.[ID], [field1], [field2], [field3]
FROM f
INNER JOIN (
-- bitwise OR the __$update_mask of the updates
-- also retrieve the last lsn of each row which should be used as the __$start_lsn of the result set
SELECT ID
, CAST(SUM(DISTINCT CAST((CASE WHEN f.[__$operation] = 4 AND f.[insert] != 1 THEN f.[__$update_mask] ELSE 0 END) as int)) as varbinary(2)) [__$update_mask]
, MAX(__$start_lsn) [max_lsn]
FROM f
GROUP BY ID
) net ON f.ID = net.ID AND f.__$start_lsn = net.[max_lsn]
To match the behavior of cdc.fn_cdc_get_net_changes_ exactly the size of the varbinary at the end should be as small as possible for all fields to fit, but a larger value wouldn't break the functionality.

SQL Server Hierarchical Sum of column

I have my database design as per the diagram.
Category table is self referencing parent child relationship
Budget will have all the categories and amount define for each category
Expense table will have entries for categories for which the amount has been spend (consider Total column from this table).
I want to write select statement that will retrieve dataset with columns given below :
ID
CategoryID
CategoryName
TotalAmount (Sum of Amount Column of all children hierarchy From BudgetTable )
SumOfExpense (Sum of Total Column of Expense all children hierarchy from expense table)
I tried to use a CTE but was unable to produce anything useful. Thanks for your help in advance. :)
Update
I just to combine and simplify data I have created one view with the query below.
SELECT
dbo.Budget.Id, dbo.Budget.ProjectId, dbo.Budget.CategoryId,
dbo.Budget.Amount,
dbo.Category.ParentID, dbo.Category.Name,
ISNULL(dbo.Expense.Total, 0) AS CostToDate
FROM
dbo.Budget
INNER JOIN
dbo.Category ON dbo.Budget.CategoryId = dbo.Category.Id
LEFT OUTER JOIN
dbo.Expense ON dbo.Category.Id = dbo.Expense.CategoryId
Basically that should produce results like this.
This is an interesting problem. And I'm going to solve it with a hierarchyid. First, the setup:
USE tempdb;
IF OBJECT_ID('dbo.Hierarchy') IS NOT NULL
DROP TABLE dbo.[Hierarchy];
CREATE TABLE dbo.Hierarchy
(
ID INT NOT NULL PRIMARY KEY,
ParentID INT NULL,
CONSTRAINT [FK_parent] FOREIGN KEY ([ParentID]) REFERENCES dbo.Hierarchy([ID]),
hid HIERARCHYID,
Amount INT NOT null
);
INSERT INTO [dbo].[Hierarchy]
( [ID], [ParentID], [Amount] )
VALUES
(1, NULL, 100 ),
(2, 1, 50),
(3, 1, 50),
(4, 2, 58),
(5, 2, 7),
(6, 3, 10),
(7, 3, 20)
SELECT * FROM dbo.[Hierarchy] AS [h];
Next, to update the hid column with a proper value for the hiearchyid. I'll use a bog standard recursive cte for that
WITH cte AS (
SELECT [h].[ID] ,
[h].[ParentID] ,
CAST('/' + CAST(h.[ID] AS VARCHAR(10)) + '/' AS VARCHAR(MAX)) AS [h],
[h].[hid]
FROM [dbo].[Hierarchy] AS [h]
WHERE [h].[ParentID] IS NULL
UNION ALL
SELECT [h].[ID] ,
[h].[ParentID] ,
CAST([c].[h] + CAST(h.[ID] AS VARCHAR(10)) + '/' AS VARCHAR(MAX)) AS [h],
[h].[hid]
FROM [dbo].[Hierarchy] AS [h]
JOIN [cte] AS [c]
ON [h].[ParentID] = [c].[ID]
)
UPDATE [h]
SET hid = [cte].[h]
FROM cte
JOIN dbo.[Hierarchy] AS [h]
ON [h].[ID] = [cte].[ID];
Now that the heavy lifting is done, the results you want are almost trivially obtained:
SELECT p.id, SUM([c].[Amount])
FROM dbo.[Hierarchy] AS [p]
JOIN [dbo].[Hierarchy] AS [c]
ON c.[hid].IsDescendantOf(p.[hid]) = 1
GROUP BY [p].[ID];
After much research and using test data, I was able to get the running totals starting from bottom of hierarchy.
The solution is made up of two steps.
Create a scalar-valued function that will decide whether a categoryId is a direct or indirect child of another categoryId. This is given in first code-snippet. Note that a recursive query is used for this since that is the best approach when dealing with hierarchy in SQL Server.
Write the running total query that will give totals according to your requirements for all categories. You can filter by category if you wanted to on this query. The second code snippet provides this query.
Scalar-valued function that tells if a child category is a direct or indirect child of another category
CREATE FUNCTION dbo.IsADirectOrIndirectChild(
#childId int, #parentId int)
RETURNS int
AS
BEGIN
DECLARE #isAChild int;
WITH h(ParentId, ChildId)
-- CTE name and columns
AS (
SELECT TOP 1 #parentId, #parentId
FROM dbo.Category AS b
UNION ALL
SELECT b.ParentId, b.Id AS ChildId
FROM h AS cte
INNER JOIN
Category AS b
ON b.ParentId = cte.ChildId AND
cte.ChildId IS NOT NULL)
SELECT #isAChild = ISNULL(ChildId, 0)
FROM h
WHERE ChildId = #childId AND
ParentId <> ChildId
OPTION(MAXRECURSION 32000);
IF #isAChild > 0
BEGIN
SET #isAChild = 1;
END;
ELSE
BEGIN
SET #isAChild = 0;
END;
RETURN #isAChild;
END;
GO
Query for running total starting from bottom of hierarchy
SELECT c.Id AS CategoryId, c.Name AS CategoryName,
(
SELECT SUM(ISNULL(b.amount, 0))
FROM dbo.Budget AS b
WHERE dbo.IsADirectOrIndirectChild( b.CategoryId, c.Id ) = 1 OR
b.CategoryId = c.Id
) AS totalAmount,
(
SELECT SUM(ISNULL(e.total, 0))
FROM dbo.Expense AS e
WHERE dbo.IsADirectOrIndirectChild( e.CategoryId, c.Id ) = 1 OR
e.CategoryId = c.Id
) AS totalCost
FROM dbo.Category AS c;

Sql 2000 data format using a group by query

There is a table with below mentioned sample data.I need to get a result set in a specific format.
Original table
org type value
a 1 1000
a 2 200
b 1 1020
b 2 100
c 1 890
c 2 20
Required Result set
org value
a (1000-2000)/1000
b (1020-100)/1020
c (890-20)/890
How to achieve this using SQL 2000.Do I ahve to pivot to get the org wise values as shown above?
No
select t1.org, (t1.value - t2.value) / t1.value
From myTable t1
inner join myTable t2 on t1.org = t2.org and t1.type = 1 and t2.type = 2
should do it, given there's always a type 1 and type 2 for each org
CREATE TABLE #tmp (
Org VARCHAR(50)
,[type] INT
,value DECIMAL(18, 2)
)
INSERT #tmp (Org,[type],value)
VALUES ('a',1,1000)
,('a',2,200)
,('b',1,1020)
,('b',2,100)
,('c',1,890)
,('c',2,20)
SELECT t1.Org
,(t1.Value-t2.value) / t1.Value [Math Done]
,'('+convert(varchar(10),t1.Value) +'-'
+ convert(varchar(10),t2.Value)+')/'
+convert(varchar(10),t1.Value) [Math Shown]
FROM (SELECT Org, Value FROM #tmp WHERE Type = 1) t1
INNER JOIN (SELECT Org, Value FROM #tmp WHERE Type = 2) t2
ON t1.Org = t2.Org
DROP TABLE #tmp
Results:
Org Math Done Math Shown
a 0.8000000000000000000 (1000.00-200.00)/1000.00
b 0.9019607843137254901 (1020.00-100.00)/1020.00
c 0.9775280898876404494 (890.00-20.00)/890.00
Working example, if you want to display the math instead of just doing it.

Set an entire field row value if equal to a single value

Sorry for the confusing title, but I couldn't find a better way to explain it.
I have some results where one row should populate an entire row if a specific code is found.
Here's the data:
PartID CustomerID Status Qty Notes
1000 900123 1 10 NULL
911 900123 1 5 NULL
Here's what I want it to do:
PartID CustomerID Status Qty Notes
1000 900123 1 10 911
911 900123 1 5 911
How could I write a query to give the notes field a value of 911 if one PartID has a value of 911?
EDIT: Thanks for the replies everyone, but I was hoping I could use a Select statement to accomplish this. I accomplished this by using a temp table, updating if the customer has a 911 in their order, updated only that customer's notes with 911, then queried the temp table for the data.
Try this:
UPDATE Table
SET Notes=(CASE WHEN (SELECT Count(1) FROM Table Where PartId=911)>0 THEN 911 ELSE NULL END)
or
UPDATE t
SET t.Notes= t2.PartId
FROM Table t
LEFT JOIN Table t2 on t2.PartId=911
update #t
set Notes=(select PartID from #t where PartID in(911))
select * from #t
SEE DEMO
You could use something like
update MyTable
set Notes = '911'
where (select count(1) from #MyTableVar where PartID = 911) > 0
For example:
DECLARE #MyTableVar table( PartID int,
CustomerID int,
Status int,
Qty int,
Notes varchar(50));
insert into #MyTableVar(PartID, CustomerID, Status, Qty, Notes)
values (1000, 900123, 1, 10, null)
insert into #MyTableVar(PartID, CustomerID, Status, Qty, Notes)
values (911, 900123, 1, 5, null)
select * from #MyTableVar
update #MyTableVar
set Notes = '911'
where (select count(1) from #MyTableVar where PartID = 911) > 0
select * from #MyTableVar
EDIT:
To just change the returned values, rather than updating the database you could do the following (based on the above example):
select
mtv.PartID,
mtv.CustomerID,
mtv.Status,
mtv.Qty,
case when (select count(1) from #MyTableVar where PartID = 911) > 0
then '911'
else mtv.Notes
end as Notes
from
#MyTableVar mtv
select PartID, CustomerID, Status, Qty,
case when exists(select * from notes where PartID = 911) then '911' else Notes end Notes
from notes
I would recommend you to split the logic in two separate moves:
--1. check condition
declare #IsPartIDDetected int = 0;
if exists (select PartID from Notes where PartID = 911 )
set #IsPartIDDetected = 1;
--2. get filteredoutput
select PartID, CustomerID, Status, Qty,
case when #IsPartIDDetected = 1 then '911' else COALESCE(Notes,'') end as Notes
from Orders
This solution has an optimal execution plan and will cost less RAM.
COALESCE command added as an example of NULL values processed.
You can also wrap it into single CTE statement:
WITH partCondition as (
select top 1 PartID as conditon from Notes where PartID = 911
)
select PartID, CustomerID, Status, Qty,
case
when exists ( select * from partCondition )
then 911 --conditon met
else Notes end --condition NOT met
as Notes
from Orders;
This will help to lower execution costs.
Not clear what one partID means
Do you mean CustomerID with one partID of 911?
run these two statements:
update customer
set notes = 911
where partID = 911
and notes <> 911;
update c2
set c2.notes = 911
from customer c1
join customer c2
on c2.CustomerID = c1.CustomerID
and c1.partID = 911
and c2.partID <> 911
and (c2.notes <> 911 or c2.notes is null);
this one statement might do it but not sure it would be faster:
update c2
set c2.notes = 911
from customer c1
join customer c2
on c2.CustomerID = c1.CustomerID
and c1.partID = 911
and (c2.notes <> 911 or c2.notes is null);

Resources