I have two tables:
Table1:
id fee1 fee2
1 0.00 0.00
2 0.00 0.00
Table2:
id fee_no fee
1 A 10.00
1 B 20.00
2 A 80.00
2 B 90.00
SQL:
update a
set a.fee1 = isnull(a.fee1, 0)
+ (case when b.fee_no ='A'
then cast(isnull(b.fee, 0) as decimal(30, 2))
else 0 end),
a.fee2 = isnull(a.fee2, 0)
+ (case when b.fee_no ='B'
then cast(isnull(b.fee, 0) as decimal(30,2))
else 0 end)
from table1 a
inner join table2 b on a.id = b.id
After I executed this SQL, only fee1 of table1 is updated, while fee2 is not. Finally I used two SQL statements to update fee1 and fee2 respectively.
But why doesn't this SQL statement work?
Here is create table statement:
create table table1(
id int null,
fee1 dec(30,2) null,
fee2 dec(30,2) null
)
insert into table1 (id,fee1,fee2)
select 1,0,0 union
select 2,0,0
create table table2(
id int null,
fee_no varchar(10) null,
fee dec(30,2) null
)
insert into table2 (id,fee_no,fee)
select 1,'A',10 union
select 1,'B',20 union
select 2,'A',80 union
select 2,'B',90
The problem with your query is that each row of table1 is updated twice. However when the second update takes place, it operates on the original data of the table. So, fee2 is set back to 0.
To properly UPDATE you need a query like this:
update a
set a.fee1 = isnull(a.fee1, 0)
+ (case when b.fee_no ='A'
then cast(isnull(b.fee, 0) as decimal(30, 2))
else 0 end),
a.fee2 = isnull(a.fee2, 0)
+ (case when c.fee_no ='B'
then cast(isnull(c.fee, 0) as decimal(30,2))
else 0 end)
from table1 a
inner join table2 b on a.id = b.id and b.fee_no = 'A'
inner join table2 c on a.id = c.id and c.fee_no = 'B'
Related
I have two queries in SQL:
Query A:
SELECT column_1, column_2, column_3 FROM a
Query B
SELECT column_1, column_2, column_3 FROM b
I want to add another column in a such that the column represent the SUM of all values from b.column_3 where b.column_1 = a.column_1
How can this be done in SQL Server?
This will give you all rows you needed:
SELECT *
FROM a
LEFT JOIN b -- left join because for some of the rows in "a" there will be not row in "b"
ON a.column_1 = b.column_1 -- your first condition
OR a.column_2 = b.column_2 -- your second condations
Then we just need to perform the sum:
SELECT a.column_1
,a.column_2
,a.column_3
,SUM(CASE WHEN b.column_1 IS NOT NULL AND a.column_1 = b.column_1 THEN b.column_3 ELSE 0 END)
,SUM(CASE WHEN b.column_1 IS NOT NULL AND a.column_2 = b.column_2 THEN b.column_3 ELSE 0 END)
FROM a
LEFT JOIN b
ON a.column_1 = b.column_1
OR a.column_2 = b.column_2
GROUP BY a.column_1
,a.column_2
,a.column_3
A join with aggregation should work here:
SELECT
a.column_1,
a.column_2,
a.column_3,
b.b_sum
FROM a
LEFT JOIN
(
SELECT column_1, SUM(column_3) AS b_sum
FROM b
GROUP BY column_1
) b
ON a.column_1 = b.column_1;
I need to get percentage of nulls for a given column in a table. The table contains close to 368081344 records as of now in table. Number of records will increase by 20 million each day. Below is the query am using.
SELECT (COUNT_BIG(column)/ count_big(*)) * 100
from <table>
Then, I perform 100 - above output to fetch the required output
Please let me know best possible solution which can yield faster result
Have you tried the below method :
DECLARE #T TABLE
(
Id INT
)
;WITH CTE
AS
(
SELECT
SeqNo = 1,
NULL "Val"
UNION ALL
SELECT
SeqNo = SeqNo+1,
Val
FROM CTE
WHERE SeqNo<100
)
INSERT INTO #T(Id)
SELECT Val FROM CTE
UNION ALL
SELECT SeqNo FROM CTE
SELECT
TotCount = COUNT(1),
ValCount = SUM(CASE WHEN Id IS NULL THEN 0 ELSE 1 END),
NullCount = SUM(CASE WHEN Id IS NOT NULL THEN 0 ELSE 1 END),
NullPercent = (CAST(SUM(CASE WHEN Id IS NOT NULL THEN 0 ELSE 1 END) AS FLOAT)/CAST(COUNT(1) AS FLOAT))*100
FROM #T
Partial answer only. Not sure how to get the count for a specific column
You can speed up the total row count using this query.
SELECT P.ROWS
FROM SYS.OBJECTS AS O INNER JOIN SYS.PARTITIONS AS P
ON O.OBJECT_ID = P.OBJECT_ID
WHERE O.NAME = 'PARENT' AND
P.INDEX_ID < 2
ORDER BY O.NAME
I have a scenario where I want to update multiple columns of first table from multiple rows of second table. But the case only updates from first row from second table.
Any help to achieve this with out using cursor?
create table Table1 (ColA varchar(20), ColB varchar(20), ColC varchar(20))
insert into Table1 values (1, null, null)
create table Table2 (ColA varchar(20), ColB varchar(20), ColValue varchar(20))
insert into Table2 values (1, 2, 'X'), (1, 3, 'Y')
update Table1
set ColB = case
when T1.ColB = 2
then T1.ColValue
else T0.ColB
end,
ColC = case
when T1.ColB = 3
then T1.ColValue
else T0.ColC
end
from Table1 T0
inner join Table2 T1 on T0.ColA = T1.ColA
select * from Table1
Looking at the result, the statement has updated only ColB
UPDATE Table1
SET ColB = CASE WHEN T1.ColValue IS NOT NULL
THEN T1.ColValue
ELSE T0.ColB
END,
ColC = CASE WHEN T2.ColValue IS NOT NULL
THEN T2.ColValue
ELSE T0.ColC
END
FROM Table1 T0
INNER JOIN Table2 T1 ON T0.ColA = T1.ColA AND T1.ColB = 2
INNER JOIN Table2 T2 ON T0.ColA = T2.ColA AND T2.ColB = 3
Issue is : you are trying to update same row multiple times in the same query.
you can user Merge statement
refer
https://connect.microsoft.com/SQLServer/feedback/details/321926/merge-will-update-row-more-than-once-if-target-is-view-with-an-instead-of-trigger
There a number of ways that this could be done, here is one:
UPDATE t1
SET ColB = t2.ColValue
FROM Table1 AS t1
JOIN (SELECT * FROM Table2 WHERE Table2.ColB = 2) AS t2
ON t1.ColA = t2.ColA;
UPDATE t1
SET ColC = t2.ColValue
FROM Table1 AS t1
JOIN (SELECT * FROM Table2 WHERE Table2.ColB = 3) AS t2
ON t1.ColA = t2.ColA;
This does involve two passes over each table, but the syntax is very simple which is always a bonus.
If it is necessary to use a single statement try like this:
UPDATE t1
SET ColB = ISNULL((SELECT TOP 1 ColValue FROM Table2 WHERE Table2.ColA = t1.ColA AND Table2.ColB = 2), t1.ColB),
ColC = ISNULL((SELECT TOP 1 ColValue FROM Table2 WHERE Table2.ColA = t1.ColA AND Table2.ColB = 3), t1.ColC),
...
FROM Table1 AS t1;
Using ISNULL() to avoid updating where there is nothing in Table2.
I think this should do it:
update Table1
set ColB =
(
select max(case when t2.ColB = 2 then t2.ColValue else t1.ColB end)
from Table1 t1
inner join Table2 t2 on t1.ColA = t2.ColA
),
ColC =
(
select max(case when t2.ColB = 3 then t2.ColValue else t1.ColB end)
from Table1 t1
inner join Table2 t2 on t1.ColA = t2.ColA
)
I have 4 tables in a database. The warehouse contains boxes owned by clients, and the boxes have files in them. There is a Client table, a Warehouse table, a Boxes table, and a Files table.
So the Client table has WarehouseID as a foreign key, the Boxes table has ClientID as a foreign key, and the Files table has BoxID as a foreign key. I want to count the number of boxes and files that each client has in my query, as well as the number of boxes that are in and out of the warehouse. A Status field on the Boxes and Files tables determines if the boxes and files are in or out of the warehouse. I run the following query on the boxes and the numbers are correct:
SELECT
[c].[ClientID],
[c].[Name] AS [ClientName],
[w].[Name] AS [WarehouseName],
COUNT(DISTINCT [b].[BoxID]) AS [BoxCount],
SUM(CASE WHEN [b].[Status] = #IN THEN 1 ELSE 0 END)) AS [BoxesIn],
SUM(CASE WHEN [b].[Status] = #OUT THEN 1 ELSE 0 END) AS [BoxesOut],
SUM(CASE WHEN [b].[DestructionDate] <= GETDATE() THEN 1 ELSE 0 END) AS [BoxesForDestruction],
FROM [Clients] AS [c] INNER JOIN [Boxes] AS [b]
ON [c].[ClientID] = [b].[ClientID]
INNER JOIN [Warehouses] AS [w]
ON [c].WarehouseID = [w].[WarehouseID]
WHERE [c].[ClientID] = #ClientID
GROUP BY
[c].[ClientID],
[c].[Name],
[w].[Name]
This produces the output of:
ClientID | ClientName | WarehouseName | BoxCount | BoxesIn | BoxesOut | BoxesForDestruction
1 | ACME Corp. | FooFactory | 22744 | 22699 | 45 | 7888
The output of the count is correct. When I add the Files table to the INNER JOIN then the numbers get inflated. Here is the SQL:
SELECT
[c].[ClientID],
[c].[Name] AS [ClientName],
[w].[Name] AS [WarehouseName],
COUNT(DISTINCT [b].[BoxID]) AS [BoxCount],
COUNT(DISTINCT [f].[FileID]) AS [FileCount], -- *NEW*
SUM(CASE WHEN [b].[Status] = #IN THEN 1 ELSE 0 END)) AS [BoxesIn],
SUM(CASE WHEN [b].[Status] = #OUT THEN 1 ELSE 0 END) AS [BoxesOut],
SUM(CASE WHEN [b].[DestructionDate] <= GETDATE() THEN 1 ELSE 0 END) AS [BoxesForDestruction],
FROM [Clients] AS [c] INNER JOIN [Boxes] AS [b]
ON [c].[ClientID] = [b].[ClientID]
INNER JOIN [Warehouses] AS [w]
ON [c].[WarehouseID] = [w].[WarehouseID]
INNER JOIN [Files] AS [f] -- *NEW*
ON [b].[BoxID] = [f].[BoxID] -- *NEW*
WHERE [c].[ClientID] = #ClientID
GROUP BY
[c].[ClientID],
[c].[Name],
[w].[Name]
This gives me the count output below (I've omitted the first 3 columns since they're not relevant):
BoxCount | FilesCount | BoxesIn | BoxesOut | BoxesForDestruction
19151 | 411961 | 411381 | 580 | 144615
The FilesCount is correct, but the other numbers are off. I know why this is happening, but I'm not sure how to fix it. The extra rows are created due to the multiple rows returned by the join on the boxes and files. When performing the SUM, the extra rows inflate the count. Since there is only one row for the warehouse, that join doesn't affect the count. How do I modify my query to get the correct number of files and boxes in and out of the warehouse?
A join repeats each row in the left hand table for each row in the right hand table. If you combine multiple joins some rows will be double counted. A solution is to move the count to a subquery. For example:
select *
from table1 t1
join (
select table1_id
, count(*)
from table2
group by
table1_id
) t2
on t2.table1_id = t1.id
join (
select table1_id
, count(*)
from table3
group by
table1_id
) t3
on t3.table1_id = t1.id
As mentioned by Andomar, I included "as myColumnOne" and "myColumnTwo" besides Count(*), as it is required on SQL Server 2018:
select *
from table1 t1
join (
select table1_id
, count(*) as myColumnOne
from table2
group by
table1_id
) t2
on t2.table1_id = t1.id
join (
select table1_id
, count(*) as myColumnTwo
from table3
group by
table1_id
) t3
on t3.table1_id = t1.id
I have 3 columns to base my JOIN on -> ID, Account, Cust. There can be multiple rows containing the same ID value.
I want to prioritise my JOIN on 1) ID, 2) Account, 3) Cust.
So in the example below, the UserCode that should be populated in #UserData should be 'u11z' as all columns contain a value.
How do I do this? Below my code to date...
UPDATE #UserData
SET UserCode = ur.UserCode
FROM #UserData uA
INNER JOIN UserReference ur
ON uA.ID = ur.ID
AND ((ua.Account = ur.Account) OR (ur.Account = ur.Account))
AND ((ua.Cust = ur.Cust) OR (ur.Cust = ur.Cust))
UserReference TABLE:
Cust Account ID UserCode
234 NULL 9A2346 u12x
234 Test 9A2346 u11z
NULL NULL 9A2346 u30s
#UserData TABLE:
Cust Account ID UserCode
234 Test 9A2346 NULL
Thanks!
You can try the following. I joined tables, counted the number of matches, and ranked them. Then select rank 1.
; with userCte (userCodeA, userCodeB, rank)
as
(
select a.usercode, b.usercode,
rank() over (partition by a.id order by case when a.cust = b.cust then 1 else 0 end +
case when a.account = b.account then 1 else 0 end +
case when a.id = b.id then 1 else 0 end desc) as rank
from userdata a
join userreference b
on a.id = b.id or a.account = b.account or a.id = b.id
)
select * from userCte
--update userCte
--set userCodeA = userCodeB
where rank = 1
Is this what you want? It is difficult to understand what you are asking for.
USE tempdb;
CREATE TABLE UserReference
(
ID VARCHAR(255) NULL
, Account VARCHAR(255) NULL
, Cust INT NULL
, UserCode VARCHAR(255)
);
INSERT INTO UserReference VALUES ('9A2346', NULL, 234, 'A');
INSERT INTO UserReference VALUES ('9A2346', 'TEST', 234, 'B');
INSERT INTO UserReference VALUES ('9A2346', NULL, NULL, 'C');
DECLARE #UserData TABLE
(
ID VARCHAR(255) NULL
, Account VARCHAR(255) NULL
, Cust INT NULL
, UserCode VARCHAR(255)
);
INSERT INTO #UserData
SELECT UR.ID, UR.Account, UR.Cust, NULL
FROM dbo.UserReference UR;
UPDATE #UserData
SET UserCode = ur.UserCode
FROM #UserData uA
INNER JOIN UserReference ur
ON uA.ID = ur.ID
AND ua.Account = ur.Account
AND ua.Cust = ur.Cust;
SELECT *
FROM #UserData;
Results of the last SELECT :
If I understood your question correctly...
And if any col in a row having a null value will drop the priority then you can use a query something like below to check the count of null values in a row, this might not be a complete answer, but a possible approach...
SELECT count(*)
FROM TableName
WHERE Col1 IS NULL and Col2 is null