Output data after updating SQL - sql

imagine there are 2 tables :
T_Customer (p_customer_id, name, prename, country, age)
and
T_SomeInfo (f_customer_id, somebit, otherbit)
Now I want to update 1 random somebit and OUTPUT updated T_Customer, which belongs to f_customer_id of effected row.
Atm I've following statement :
UPDATE randombit SET randombit.somebit= 1
OUTPUT inserted.f_customer_id
FROM
(
SELECT TOP 1 * FROM T_SomeInfo
WHERE somebit= 0 AND otherbit = 0
ORDER BY NEWID()
) AS randombit
So I f_customer_id of my updated row.
But I'm not able to build a valid statement to OUTPUT a value from another table.
This is a statement I tried without success:
UPDATE randombit SET randombit.somebit= 1
OUTPUT customer.*
FROM T_Customer AS customer
WHERE customer.f_customer_id = inserted.f_customer_id
FROM
(
SELECT TOP 1 * FROM T_SomeInfo
WHERE somebit= 0 AND otherbit = 0
ORDER BY NEWID()
) AS randombit
Is there any solution to update and output (with INNER JOIN or SELECT) into one statement?
EDIT as example:
There are 2 customers :
T_Customer (1, "Smith", "John", "country", 10)
T_Customer (2, "John", "William", "country2", 20)
actually a update
UPDATE randombit SET randombit.somebit= 1
OUTPUT inserted.f_customer_id
FROM
(
SELECT TOP 1 * FROM T_SomeInfo
WHERE somebit= 0 AND otherbit = 0
ORDER BY NEWID()
) AS randombit
will output (if he's the random winner):
1
But I want to see
1, "Smith", "John", "country", 10

Related

SQL Server exclusive select on column value

Let's say I am returning the following table from a select
CaseId
DocId
DocumentTypeId
DocumentType
ExpirationDate
1
1
1
I797
01/02/23
1
2
2
I94
01/02/23
1
3
3
Some Other Value
01/02/23
I want to select ONLY the row with DocumentType = 'I797', then if there is no 'I797', I want to select ONLY the row where DocumentType = 'I94'; failing to find either of those two I want to take all rows with any other value of DocumentType.
Using SQL Server ideally.
I think I'm looking for an XOR clause but can't work out how to do that in SQL Server or to get all other values.
Similar to #siggemannen answer
select top 1 with ties
case when DocumentType='I797' then 1
when DocumentType='I94' then 2
else 3
end gr
,docs.*
from docs
order by
case when DocumentType='I797' then 1
when DocumentType='I94' then 2
else 3
end
Shortest:
select top 1 with ties
docs.*
from docs
order by
case when DocumentType='I797' then 1
when DocumentType='I94' then 2
else 3
end
Something like this perhaps:
select *
from (
select t.*, DENSE_RANK() OVER(ORDER BY CASE WHEN DocumentType = 'I797' THEN 0 WHEN DocumentType = 'I94' THEN 1 ELSE 2 END) AS prioorder
from
(
VALUES
(1, 1, 1, N'I797', N'01/02/23')
, (1, 2, 2, N'I94', N'01/02/23')
, (1, 3, 3, N'Some Other Value', N'01/02/23')
, (1, 4, 3, N'Super Sekret', N'01/02/23')
) t (CaseId,DocId,DocumentTypeId,DocumentType,ExpirationDate)
) x
WHERE x.prioorder = 1
The idea is to rank rows by 1, 2, 3 depending on document type. Since we rank "the rest" the same, you will get all rows if I797 and I94 is missing.
select * from YourTable where DocumentType = 'I797'
union
select * from YourTable t where DocumentType = 'I94' and (not exists (select * from YourTable where DocumentType = 'I797'))
union
select * from YourTable t where (not exists (select * from YourTable where DocumentType = 'I797' or DocumentType = 'I94' ))

How do i create a DB2 UNION query using variables from a list

So i have a union query like:
select count(id)
from table 1
where membernumber = 'x'
and castnumber = 'y'
union
select count(id)
from table 1
where membernumber = 'x'
and castnumber = 'y'
union
etc...
There will be over 200 unions coming from a list 2x 200 table with values for x and y in each row. So each union query has to get the value of x and y from the corresponding row (not in any particular order).
How can i achieve that ?
Thanks
Try this:
DECLARE GLOBAL TEMPORARY TABLE
SESSION.PARAMETERS
(
MEMBERNUMBER INT
, CASTNUMBER INT
) DEFINITION ONLY WITH REPLACE
ON COMMIT PRESERVE ROWS NOT LOGGED;
-- Insert all the the constants in your application with
INSERT INTO SESSION.PARAMETERS
(MEMBERNUMBER, CASTNUMBER)
VALUES (?, ?);
-- I don't know the meaning of the result you want to get
-- but it's equivalent
select distinct count(t.id)
from table1 t
join session.parameters p
on p.membernumber = t.membernumber
and p.castnumber = t.castnumber
group by t.membernumber, t.castnumber;

Query based on multiple rows and multiple columns

Database : Azure SQL server 2019, .net core 3.0
I'm using stored procedure for querying data.
--Table Structure
create table yourtable
(
id int,
class int,
islab bit,
isschool bit
);
insert into yourtable
values (1, 1, 1, 1),
(1, 2, 1, 1),
(1, 3, 1, 1),
(2, 1, 1, 0),
(2, 2, 1, 1),
(2, 3, 1, 1)
Now if I want a query to return all unique Id's where class = 1 and 2 and islab = 1 and isschool = 1, it should return only Id =1 because
a) Id=1 has both classes i,e (1,2) and in both classes islab = 1 and isschool = 1
b) Id=2 is not true for this condition because in classes 1 value for isschool = 0
Can you help me write this query? Currently I'm getting all row for input classes than in c# using list check all conditions. It's working but I want do all in SQL
Also I think using cursor in stored procedure I can have same result as in C# but in C# it's easy as we have Collection and various methods like intersect between lists and so on.
Assuming that islab and isschool are actually a bit (as bool doesn't exist in SQL Server), one method would be to use a HAVING with conditional aggregation. So, for the first one, you would do the following:
SELECT id
FROM dbo.yourtable
WHERE islab = 1
AND isschool = 1
GROUP BY id
HAVING COUNT(CASE class WHEN 1 THEN 1 END) = 1
AND COUNT(CASE class WHEN 2 THEN 1 END) = 1;
The question is not clear, but based on your expected output, I think you want a query that, for a given set of classes, finds the ids where there are no records with isschool = 0 or islab = 0. You can do this with a NOT EXISTS condition:
WITH mytab AS
(
SELECT *
FROM yourtable
WHERE class IN (1,2,3) -- Change this line to get your 3 different outputs
)
SELECT DISTINCT id
FROM mytab t1
WHERE NOT EXISTS
(
SELECT *
FROM mytab t2
WHERE t1.id = t2.id
AND (t2.islab = 0 OR t2.isschool = 0)
)
For class IN (1,2) this returns id 1.
For class IN (2,3) this returns ids 1 and 2.
For class IN (1,2,3) this returns id 1.
The CTE limits to the classes we want to consider. The subquery in the NOT EXISTS finds ids that should be eliminated because either isschool = 0 or islab = 0.
An alternative way of doing this, using a LEFT JOIN instead of the NOT EXISTS condition is:
WITH mytab AS
(
SELECT *
FROM yourtable
WHERE class IN (1,2,3) -- Change this line to get your 3 different version
)
SELECT DISTINCT t1.id
FROM mytab t1 LEFT OUTER JOIN mytab t2
on t1.id = t2.id AND (t2.islab = 0 OR t2.isschool = 0)
WHERE t2.id is null

How to replace one digit in a number in SQL?

I'm trying to change group of numbers to another one.
Now:
PRACTICE_ID (FK) GROUP_ID ...
0_4_200_400_0.5 4
0_4_300_300_0.5 4
0_4_400_700_0.5 4
0_5_200_400_0.5 5
0_5_900_400_0.5 5
0_5_650_400_0.5 5
Should be:
PRACTICE_ID (FK) GROUP_ID ...
*0_5_200_400_0.5 4
0_5_300_300_0.5 4
0_5_400_700_0.5 4
*0_5_200_400_0.5 5
0_5_900_400_0.5 5
0_5_650_400_0.5 5
I use the query to reach the target:
UPDATE CUSTOM_PRACTICE
SET PRACTICE_ID = REPLACE(PRACTICE_ID, '0_4', '0_5')
WHERE GROUP_ID = 4
In this case I cannot duplicate values. I was trying to use NOT EXISTS clause but didn't get result
How can I rewrite all values which have not duplicate? Like this:
PRACTICE_ID (FK) GROUP_ID ...
0_4_200_400_0.5 4
0_5_300_300_0.5 4
0_5_400_700_0.5 4
0_5_200_400_0.5 5
0_5_900_400_0.5 5
0_5_650_400_0.5 5
UPDATE CUSTOM_PRACTICE
SET PRACTICE_ID = REPLACE(PRACTICE_ID, '0_4', '0_5')
WHERE GROUP_ID = 4 AND REPLACE(PRACTICE_ID, '0_4', '0_5') NOT IN
(SELECT PRACTICE_ID FROM CUSTOM_PRACTICE)
SQL Server has a very convenient function for this called STUFF(). One way of doing what you want uses NOT EXISTS:
UPDATE CUSTOM_PRACTICE
SET PRACTICE_ID = STUFF(PRACTICE_ID, 3, 1, '5')
WHERE PRACTICE_ID LIKE '0_4%' AND
NOT EXISTS (SELECT 1
FROM CUSTOM_PRACTICE CP2
WHERE CP2.PRACTICE_ID = STUFF(PRACTICE_ID, 3, 1, '5') AND
STUFF(PRACTICE_ID, 3, 1, '5')
);
Note that REPLACE(PRACTICE_ID, '0_4', '0_5') does not really do what yo want, because:
REPLACE('0_4_200_400_0.5', '0_4', '0_5')
returns:
'0_5_200_500_0.5'
not:
'0_4_200_400_0.5'
However, I think I like using an updatable CTE, because the logic only appears in one place:
with toupdate as (
select cp.*
(case when practice_id like '0_4%' then stuff(practice_id, 3, 1, '5')
else practice_id
end) as new_practice_id
from custom_practice cp
)
update t
set practice_id = new_practice_id
from (select t.*, count(*) over (partition by new_practice_id) as cnt
from toupdate
)
where practice_id <> new_practice_id and cnt = 1;

SQL sub query with complex criteria

I have a table like this:
TransId. LayerNo. AccountId.
100. 1. 2.
100. 2. 3.
120. 1. 5.
120. 2. 6.
120. 3. 12.
70. 1. 2.
I want to find transId(s) where:
(LayerNo = 1 and (accountId = 2 or 5))
and
(LayerNo = 2 and (accountId = 3 or 6))
And result set would be row no 1,2,3,4.
How could I write query to get the result?
My database is SQL server 2008 r2
Thanks in advance
Nima
SELECT TransId
FROM your_table
WHERE ( layerno = 1
AND accountid IN ( 2, 5 ) )
INTERSECT
SELECT TransId
FROM your_table
WHERE ( layerno = 2
AND accountid IN ( 3, 6 ) )
One approach is to ensure that each transID must have two records that satisfy the conditions you outlined.
SELECT * FROM
TABLE
WHERE TransID IN(
SELECT TransId
FROM table
WHERE ( layerno = 1
AND accountid IN ( 2, 5 ) )
OR ( layerno = 2
AND accountid IN( 3, 6 ) )
GROUP BY
TransId
HAVING Count(*) = 2
)
However this could be a problem if you can have multple records where layerno = 1. So you can use self joins instead to ensure the criteria.
SELECT DISTINCT a.transid
FROM table a
INNER JOIN table b
ON a.transid = b.transid
INNER JOIN table c
ON a.transid = c.transid
WHERE b.layerno = 1
AND accountid IN ( 2, 5 )
AND c.layerno = 2
AND accountid IN ( 3, 6 )
That said Martin's INTERSECT approach is probably the best
Do you mean:
SELECT
TransId,
LayerNo,
AccountId
FROM Table
WHERE (LayerNo = 1 AND AccountId IN (2, 5)) OR
(LayerNo = 2 AND AccountId IN (3, 7))
create table #temp
( rowId Int Identity(1,1), transId int)
INSERT INTO #temp(transId)
select TransId
from TableName
where (layerNo = 1 and accountID IN (2, 5))
OR (layerNo = 2 and accountId IN (3, 6))
select * from #temp
SELECT
base.TransId,
base.LayerNo,
base.AccountId
FROM TableX AS base
JOIN TableX AS a
ON a.TransId = base.TransId
AND a.LayerNo = 1 AND a.AccountId IN (2, 5)
JOIN TableX AS b
ON b.TransId = base.TransId
AND b.LayerNo = 2 AND b.AccountId IN (3, 7)
WHERE (base.LayerNo = 1 AND base.AccountId IN (2, 5))
OR (base.LayerNo = 2 AND base.AccountId IN (3, 7))
This intersection is empty. If you take the values where LayerNo = 1 and LayerNo = 2 and intersect them their intersection is empty because these events are mutually exclusive. I believe this error comes from how the question was originally stated. I might be wrong but the predicate should have been
(LayerNo = 1 and (accountId = 2 or 5)) OR (LayerNo = 2 and (accountId = 3 or 6))
Replace the AND with an OR. If the predicate was stated correctly then the intersect is correct but will always be empty.
SELECT *
FROM table
WHERE (LayerNo = 1 AND (AccountID = 2 OR AccountID = 5))
OR (LayerNo = 2 AND (AccountID = 3 OR AccountID = 6))