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);
Related
I need to rename the column base on the value I give in it.
SELECT TOP 1
[Pratice_Id] = [OID]// id
, [Province] = '' //province
, [Country] = 'US'// country
FROM [tbl_Office]
if the country equals to US I want to change columnName [Province] to State
I would strongly suggest a column STATE_PROVINCE, however, if you MUST and you are not opposed to a temp table, perhaps something like this
Example
Select Top 1 *
Into #Temp
From YourTable
Where ...
Order By ...
If ( Select Top 1 Country From #Temp ) = 'US'
Select [Pratice_Id]
,[State] = [Province]
,[Country]
From #Temp
Else
Select * from #Temp
Returns
Pratice_Id State Country
1 RI US
Personally, I would provide both
Declare #YourTable table ([Pratice_Id] int,[Province] varchar(50),[Country] varchar(50))
Insert Into #YourTable values
(1,'RI','US')
,(2,'Alberta','Canada')
Select [Pratice_Id]
,[State] = case when [Country]='US' then Province else '' end
,[Province] = case when [Country]<>'US' then Province else '' end
,[Country]
From #YourTable
Returns
Pratice_Id State Province Country
1 RI US
2 Alberta Canada
I have a number of joined "system versioned" tables, e.g. Person, PhoneNumber and EmailAddress
The Person will only have one PhoneNumber and one EmailAddress at a time.
The PhoneNumber and EmailAddress will not usually be updated outside of a transaction that updates all 3 at once. (But they can be updated independently, just not in the normal scenario)
E.g. if I change the phone number, then all 3 records will be issued an update in the same transaction, hence giving them all the same "start time" in the history table.
Let's say I insert a person and then change the person's name, email address and phone number in 2 transactions:
DECLARE #Id TABLE(ID INT)
DECLARE #PersonId INT
-- Initial insert
BEGIN TRANSACTION
INSERT INTO Person (Name) OUTPUT inserted.PersonId INTO #Id VALUES ('Homer')
SELECT #PersonId = Id FROM #Id
INSERT INTO EmailAddress (Address, PersonId) VALUES ('homer#fake', #PersonId)
INSERT INTO PhoneNumber (Number, PersonId) VALUES ('999', #PersonId)
COMMIT TRANSACTION
-- Update
WAITFOR DELAY '00:00:02'
BEGIN TRANSACTION
UPDATE Person SET Name = 'Kwyjibo' WHERE PersonID = #PersonId
UPDATE EmailAddress SET Address = 'kwyjibo#fake' WHERE PersonID = #PersonId
UPDATE PhoneNumber SET Number = '000' WHERE PersonID = #PersonId
COMMIT TRANSACTION
Now I select from the view (just an inner join of the tables) using a temporal query:
SELECT * FROM vwPerson FOR SYSTEM_TIME ALL
WHERE PersonId = #PersonId
ORDER BY SysStartTime DESC
And I get returned a row for every combination of edit!
How can I query this view (if at all possible) to only return 1 row for the updates that were made in the same transaction?
I could add a WHERE clause to match all the SysStartTimes, however that would exclude those cases where a table was updated independently of the other 2.
Because of the independent updates, you actually first have to "reconstruct" a timeline, onto which you can join the data. A "sketch" of this follows, obviously don't have your actual table defns so untested:
;WITH AllTimes as (
SELECT PersonId,SysStartTime as ATime FROM Person
UNION
SELECT PersonId,SysEndTime FROM Person
UNION
SELECT PersonId,SysStartTime FROM EmailAddress
UNION
SELECT PersonId,SysEndTime FROM EmailAddress
UNION
SELECT PersonId,SysStartTime FROM PhoneNumber
UNION
SELECT PersonId,SysEndTime FROM PhoneNumber
), Ordered as (
SELECT
PersonId, ATime, ROW_NUMBER() OVER (PARTITION BY PersonId ORDER BY Atime) rn
FROM
AllTimes
), Intervals as (
SELECT
p1.PersonId,
o1.ATime as StartTime,
o2.ATime as EndTime
FROM
Ordered o1
inner join
Ordered o2
on
o1.PersonId = o2.PersonId and
o1.rn = o2.rn - 1
)
SELECT
* --TODO - Columns
FROM
Intervals i
inner join
Person p
on
i.PersonId = p.PersonId and
i.StartTime < p.SysEndTime and
p.SysStartTime < i.EndTime
inner join
Email e
on
i.PersonId = e.PersonId and
i.StartTime < e.SysEndTime and
e.SysStartTime < i.EndTime
inner join
PhoneNumber pn
on
i.PersonId = pn.PersonId and
i.StartTime < pn.SysEndTime and
pn.SysStartTime < i.EndTime
With appropriate filters if you just want one persons details, the optimizer will hopefully work it out. There may be additional filters for the joins that I've missed out also.
Hopefully you can see how the 3 CTEs construct the timeline. We take advantage of UNION eliminating duplicates in the first one.
I'm trying to group a SELECT like you'd normally do - AND at the same time make a new "shared/aggregate group" adding that to the original result-set without a secondary SELECT and UNION.
The secondary SELECT and UNION is out of the question since the real use of this is with some very big tables, with a lot of joins, so it would be waay to slow. So the UNION way is definitely out of the question.
I've tried my best to illustrate this with the following simplified example:
BEGIN TRAN
CREATE TABLE #MyTable
(
id INT,
name VARCHAR(255)
)
INSERT INTO #MyTable VALUES (1,'cola');
INSERT INTO #MyTable VALUES (2,'cola');
INSERT INTO #MyTable VALUES (3,'cola');
INSERT INTO #MyTable VALUES (4,'fanta');
INSERT INTO #MyTable VALUES (5,'fanta');
INSERT INTO #MyTable VALUES (6,'fanta');
INSERT INTO #MyTable VALUES (7,'water');
INSERT INTO #MyTable VALUES (8,'water');
INSERT INTO #MyTable VALUES (9,'water');
INSERT INTO #MyTable VALUES (10,'cola');
INSERT INTO #MyTable VALUES (11,'cola');
SELECT
CASE
WHEN name = 'cola' OR name = 'fanta'
THEN 'soda'
ELSE
name
END as name,
COUNT(distinct id) as count
FROM #MyTable
GROUP BY name
ROLLBACK TRAN
Actual output:
soda 5
soda 3
water 3
Desired output:
cola 5
fanta 3
soda 8 <- this is the "shared/aggregate group"
water 3
As Panagiotis Kanavos correctly pointed out in the comment above, this can be done using ROLLUP:
BEGIN TRAN
CREATE TABLE #BeverageType
(
name VARCHAR(255)
)
INSERT INTO #BeverageType VALUES ('Soda');
INSERT INTO #BeverageType VALUES ('Other');
CREATE TABLE #UserBeverage
(
id INT,
name VARCHAR(255)
)
INSERT INTO #UserBeverage VALUES (1,'cola');
INSERT INTO #UserBeverage VALUES (2,'cola');
INSERT INTO #UserBeverage VALUES (3,'cola');
INSERT INTO #UserBeverage VALUES (1,'fanta'); -- <- NOTE: user 1 drinks both cola and fanta so the as intended the user is only counted 1 time in the ROLLUP 'Soda' group (7)
INSERT INTO #UserBeverage VALUES (5,'fanta');
INSERT INTO #UserBeverage VALUES (6,'fanta');
INSERT INTO #UserBeverage VALUES (7,'water');
INSERT INTO #UserBeverage VALUES (8,'water');
INSERT INTO #UserBeverage VALUES (9,'water');
INSERT INTO #UserBeverage VALUES (10,'cola');
INSERT INTO #UserBeverage VALUES (11,'cola');
SELECT ub.name, bt.name AS groupName, COUNT(distinct id) as uniqueUserCount
FROM #UserBeverage as ub
JOIN #BeverageType as bt
ON CASE
WHEN (ub.name = 'water')
THEN 'Other'
ELSE
'Soda'
END = bt.name
GROUP BY ROLLUP(bt.name, ub.name)
ROLLBACK TRAN
Outputs:
cola Soda 5
fanta Soda 3
water Other 3
NULL Other 3
NULL Soda 7
NULL NULL 10
You should repeat CASE statement everywhere.
SELECT
CASE WHEN name = 'cola' OR name = 'fanta'
THEN 'soda' ELSE name END as name,
COUNT((CASE WHEN name = 'cola' OR name = 'fanta'
THEN 'soda' ELSE name END)) as count
FROM #MyTable
GROUP BY CASE WHEN name = 'cola' OR name = 'fanta'
THEN 'soda' ELSE name END
+-------+-------+
| name | count |
+-------+-------+
| soda | 8 |
+-------+-------+
| water | 3 |
+-------+-------+
Can I suggest to use a subquery:
SELECT name, count(*) AS count
FROM (SELECT CASE WHEN name = 'cola' OR name = 'fanta'
THEN 'soda' ELSE name END as name
FROM #MyTable) x
GROUP BY name;
If you need the aggregate as well as the individual products, then an alternative may be to use a UNION and select the aggregates as a second query.
SELECT name, count(distinct id) as count
FROM #MyTable
GROUP BY name
UNION
SELECT 'SODA', COUNT(distinct id) as count
FROM #MyTable
WHERE name = 'cola' or name ='fanta'
You might also use Søren Høyer Kristensen's summary table to get the aggregate names if you need more groupings.
I have a table with duplicate rows, however, some of the duplicate rows have columns does not contain data for the same column. How can I remove/ignore only those row where columns are blank? In some instances:
Name Employee# Location City
-----------------------------------------
BowerT 48999 NJ Foods
BowerT 48999 NJ Foods Pearl
BowerT 48999 NJ Foods Johns
BowerT 48999 NJ Foods Johns
I'm using with CTE to delete duplicate, however, if 2nd, 3rd, or 4th row has the data I need for that column, I lose it because these are greater than row 1.
;With hrEmployee as
(
Select
*,
Row_Number () Over (Partition BY Employee_Number order by Employee_Number) As RowNumber
From
[dbo].[hrEmployee]
Where
Employee_Number = '48999'
)
Delete hrEmployees
where RowNumber > 1
What am I missing?
Here is an entire example the relevant code change is:
ROW_NUMBER() OVER (PARTITION BY Employee_Number ORDER BY
CASE WHEN ISNULL(City,'') = '' THEN 1 ELSE 0 END
) as RowNumber
What that does is simply ORDER your results of what you want to keep by saying if the City is null or '' (blank) make it last. You can rank your results anyway you want by specifying different order in your ORDER BY.
DECLARE #Table AS TABLE (Name VARCHAR(10), Employee_Number INT, Location VARCHAR(20), City VARCHAR(20))
INSERT INTO #Table VALUES ('BowerT',48999,'NJ Foods',NULL)
,('BowerT',48999,'NJ Foods','Pearl')
,('BowerT',48999,'NJ Foods','Johns')
,('BowerT',48999,'NJ Foods','Johns')
SELECT *
FROM
#Table
;WITH hrEmployee AS (
SELECT
*
,ROW_NUMBER() OVER (PARTITION BY Employee_Number ORDER BY
CASE WHEN ISNULL(City,'') = '' THEN 1 ELSE 0 END
) as RowNumber
FROM
#Table
where Employee_Number = '48999'
)
DELETE
FROM
hrEmployee
WHERE
RowNumber > 1
SELECT *
FROM
#Table
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