how wors coalesce function with sum - sql

I got 2 table like it
table 1
id cost
1 200
2 300
3 500
4 700
NULL NULL
NULL NULL
table 2
1 200
2 300
3 500
4 700
5 1000
6 2500
and I did it :
sum(coalesce(table1.cost,table2.cost))
my aim is to got
200+300+500+700+1000+2500= 5200
If I do
sum(coalesce(table1.cost,table2.cost))
I got
200+300+500+700+1000+2500= 5200
OR I GOT IT ?
200+300+500+700+200+300+500+700+1000+2500= 6900
Thanks for answer, I'm curious to know how to have only 5200 and not 6900
I use snowflake SQL

Please share you queries to have more understanding of the problem.
With the mere data provided, I think you are looking for Full Outer Join as you need to include data from both tables. I have built a solution in SQL server using table variables. Both the queries gives the same result as 5200.
DECLARE #table1 table(Id INT NULL, Cost INT NULL)
DECLARE #table2 table(Id INT NULL, Cost INT NULL)
INSERT INTO #table1(Id, Cost)
VALUES(1, 200), (2, 300), (3, 500), (4, 700), (NULL, NULL), (NULL, NULL)
INSERT INTO #table2(Id, Cost)
VALUES(1, 200), (2, 300), (3, 500), (4, 700), (5, 1000), (6, 2500)
SELECT
SUM(COALESCE(t1.Cost, t2.Cost))
FROM
#table1 t1
FULL OUTER JOIN
#table2 t2
ON t1.Id = t2.Id
SELECT
SUM(COALESCE(t2.Cost, t1.Cost))
FROM
#table1 t1
FULL OUTER JOIN
#table2 t2
ON t1.Id = t2.Id

Related

How to exclude data if any record is not matching in any of the column in the another Table

I have Table A and Table B
CREATE TABLE [dbo].[TableA]
(
[ID] Int NULL,
[sk] bigint NULL,
[class] int NULL,
[Values] int NULL,
) ON [PRIMARY]
GO
INSERT INTO [dbo].[TableA] ([ID], [sk], [class], [Values])
VALUES (1, 17734, 5, 66443), (2, 17734, 4, 5456),
(3, 17734, 6, 445645), (4, 17734, 7, 4534),
(5, 16601, 4, 5443), (6, 16601, 7, 453434),
(7, 16601, 8, 76645), (8, 16601, 5, 9875)
CREATE TABLE [dbo].[TableB]
(
[ID] Int NULL,
[sk] bigint NULL,
[class] int NULL,
[Values] int NULL,
) ON [PRIMARY]
GO
INSERT INTO [dbo].[TableB] ([ID], [sk], [class], [Values])
VALUES (1, 17734, 5, 66443), (2, 17734, 4, 5456),
(3, 17734, 6, 445645), (4, 17734, 7, 4534),
(5, 16601, 4, 5443), (6, 16601, 7, 453434),
(7, 16601, 8, 76645), (8, 16601, 5, 9875)
I'm looking to join both the tables with all columns in each table. If any record is not matching then we need to remove all the SK.
For 17734 value all the columns from the both tables are matching then I need to get the values for 17734 .
For 16601 value only 1 value is not matching so I don't want to bring all the values for 16601.
SELECT DISTINCT
DC.[sk],
DC.class,
DC.Values,
DB.class AS DCC,
DB.Values AS DBC
FROM
[dbo].[TableA]
LEFT JOIN
[dbo].[TableA] DB ON DC.[sk] = DB.[sk]
AND DC.class = DB.class
AND DC.Values = DB.Values;
After joining, I get:
sk class Values class values
--------------------------------
16601 3 65567 NULL NULL
16601 4 5443 4 5443
16601 7 453434 7 453434
16601 8 76645 8 76645
17734 4 5456 4 5456
17734 5 66443 5 66443
17734 6 445645 6 445645
17734 7 4534 7 4534
Output :
sk class Values class values
--------------------------------
17734 4 5456 4 5456
17734 5 66443 5 66443
17734 6 445645 6 445645
17734 7 4534 7 4534
You must do the whole join to discover if some of the results should be removed. To reuse the query results, use a CTE and refer to it twice - once for the results and again to filter out the unwanted rows:
with table1 as (
select distinct
DC.sk,
DC.class,
DC.Values,
DB.class AS DCC,
DB.Values AS DBC
from dbo.TableA
left join dbo.TableB DB on DC.sk = DB.sk
and DC.class = DB.class
and DC.Values = DB.Values
)
select *
from table1
where sk not in (select sk from table1 where DCC is null)
BTW, you have a bug in your query: You're joining [dbo].[TableA] with itself, but it should be joined to [dbo].[TableB]
You can use EXCEPT operator to get your expected result. I will explain my query in step by step process.
Step 1: I am finding out non-matching rows using Except operator:-
SELECT A.sk, A.class, A.[Values] FROM TableA A
EXCEPT
SELECT B.sk, B.class, B.[Values] FROM TableB B
Step 2: Now I am selecting the sk of non-matching rows using:-
SELECT T.sk FROM (query of step 1)T
Step 3: I am excluding those sk from TableA using:-
SELECT * FROM TableA
WHERE sk NOT IN(query of step 2)
So our final Query is look like this:
SELECT * FROM TableA
WHERE sk NOT IN
(
SELECT T.sk FROM
(
SELECT A.sk, A.class, A.[Values] FROM TableA A
EXCEPT
SELECT B.sk, B.class, B.[Values] FROM TableB B
)T
)

Insert into one table from another two table

I have 3 tables
declare #approval_request table(req_id int, file_id int, req_type varchar(100))
insert into #approval_request
values (1, 1001, 'bulk'),
(2, 1002, 'demo'),
(3, 1003, 'bulk'),
(4, 1004, 'test');
declare #bulk_account table (file_id int, account varchar(50));
insert into #bulk_account
values (1001, '501'), (1002, '401'),
(1001, '502'), (1002, '402'),
(1001, '503'), (1002, '403');
I want to get all file_id from approval_request table where type='bulk' then
all accounts for corresponding file_id from second table(bulk_account ) and insert into third table as below.
declare #approval_bulk_account table
(
req_id int,
account varchar(50)
);
so the new table data will be
(req_id, account)
1 501
1 502
1 503
insert into #approval_bulk_account
select a.req_id,b.account
from #approval_request a
inner join #bulk_account b on a.file_id = b.file_id
and a.req_type = 'bulk';
Try to insert into the third table with inner join on first 2 tables, something like this
insert into approval_bulk_account
(req_id,account)
select a.req_id , b.account
from approval_request a inner join bulk_account b
on a.file_id =b.file_id
where a.req_type ='bulk'

Help with a SQL Query

My tables:
suggestions:
suggestion_id|title|description|user_id|status|created_time
suggestion_comments:
scomment_id|text|user_id|suggestion_id
suggestion_votes:
user_id|suggestion_id|value
Where value is the number of points assigned to a vote.
I'd like to be able to SELECT:
suggestion_id, title, the number of comments and the SUM of values for that suggestion.
sorted by SUM of values. LIMIT 30
Any ideas?
You may want to try using sub queries, as follows:
SELECT s.suggestion_id,
(
SELECT COUNT(*)
FROM suggestion_comments sc
WHERE sc.suggestion_id = s.suggestion_id
) num_of_comments,
(
SELECT SUM(sv.value)
FROM suggestion_votes sv
WHERE sv.suggestion_id = s.suggestion_id
) sum_of_values
FROM suggestions s;
Test case:
CREATE TABLE suggestions (suggestion_id int);
CREATE TABLE suggestion_comments (scomment_id int, suggestion_id int);
CREATE TABLE suggestion_votes (user_id int, suggestion_id int, value int);
INSERT INTO suggestions VALUES (1);
INSERT INTO suggestions VALUES (2);
INSERT INTO suggestions VALUES (3);
INSERT INTO suggestion_comments VALUES (1, 1);
INSERT INTO suggestion_comments VALUES (2, 1);
INSERT INTO suggestion_comments VALUES (3, 2);
INSERT INTO suggestion_comments VALUES (4, 2);
INSERT INTO suggestion_comments VALUES (5, 2);
INSERT INTO suggestion_comments VALUES (6, 3);
INSERT INTO suggestion_votes VALUES (1, 1, 3);
INSERT INTO suggestion_votes VALUES (2, 1, 5);
INSERT INTO suggestion_votes VALUES (3, 1, 1);
INSERT INTO suggestion_votes VALUES (1, 2, 4);
INSERT INTO suggestion_votes VALUES (2, 2, 2);
INSERT INTO suggestion_votes VALUES (1, 3, 5);
Result:
+---------------+-----------------+---------------+
| suggestion_id | num_of_comments | sum_of_values |
+---------------+-----------------+---------------+
| 1 | 2 | 9 |
| 2 | 3 | 6 |
| 3 | 1 | 5 |
+---------------+-----------------+---------------+
3 rows in set (0.00 sec)
UPDATE: #Naktibalda's solution is an alternative solution that avoids sub queries.
I was typing the same query as potatopeelings.
But there is an issue:
Resultset after joins contains M*N rows (M-number of comments, N-number of votes, not less than 1) for each suggestion.
To avoid that you have to count distinct comment ids and divide a sum of votes by number of comments.
SELECT
s.*,
COUNT(DISTINCT c.scommentid) AS comment_count,
SUM(v.value)/GREATEST(COUNT(DISTINCT c.scommentid), 1) AS total_votes
FROM suggestions AS s
LEFT JOIN suggestion_comments AS c ON s.suggestion_id = c.suggestion_id
LEFT JOIN suggestion_votes AS v ON s.suggestion_id = v.suggestion_id
GROUP BY s.suggestion_id
ORDER BY total_votes DESC
LIMIT 30

Help with a query

Based on the following table
ID Effort Name
-------------------------
1 1 A
2 1 A
3 8 A
4 10 B
5 4 B
6 1 B
7 10 C
8 3 C
9 30 C
I want to check if the total effort against a name is less than 40 then add a row with effort = 40 - (Total Effort) for the name. The ID of the new row can be anything. If the total effort is greater than 40 then trucate the data for one of the rows to make it 40.
So after applying the logic above table will be
ID Effort Name
-------------------------
1 1 A
2 1 A
3 8 A
10 30 A
4 10 B
5 4 B
6 1 B
11 25 B
7 10 C
8 3 C
9 27 C
I was thinking of opening a cursor, keeping a counter of the total effort, and based on the logic insert existing and new rows in another temporary table.
I am not sure if this is an efficient way to deal with this. I would like to learn if there is a better way.
I think the first part could be done this way:
INSERT INTO tbl(Effort, Name)
SELECT 40 - SUM(Effort), Name
FROM tbl
GROUP BY Name
HAVING SUM(Effort) < 40)
The second part is harder. Perhaps you could do something like this instead?
INSERT INTO tbl(Effort, Name)
SELECT 40 - SUM(Effort), Name
FROM tbl
GROUP BY Name
HAVING SUM(Effort) <> 40)
What this does is, rather than making changes to your actual data, adds a row with a negative number for the Name if the total effort is > 40 hours, or a positive value if it is < 40 hours. This seems much safer for your data integrity than messing with the original values.
In SQL Server 2008, this may be done with a single MERGE statement:
DECLARE #efforts TABLE (id INT NOT NULL PRIMARY KEY, effort INT NOT NULL, name CHAR(1))
INSERT
INTO #efforts
VALUES (1, 1, 'A'),
(2, 1, 'A'),
(3, 8, 'A'),
(4, 10, 'B'),
(5, 4, 'B'),
(6, 1, 'B'),
(7, 10, 'C'),
(8, 3, 'C'),
(9, 30, 'C'),
(10, 60, 'C')
SELECT *
FROM #efforts
ORDER BY
name, id
;WITH total AS
( SELECT *
FROM #efforts e
UNION ALL
SELECT ROW_NUMBER() OVER(ORDER BY name) +
(
SELECT MAX(id)
FROM #efforts
),
40 - SUM(effort),
name
FROM #efforts
GROUP BY
name
HAVING SUM(effort) < 40
),
source AS
(
SELECT *,
(
SELECT SUM(effort)
FROM total ep
WHERE ep.name = e.name
AND ep.id <= e.id
) AS ce,
COALESCE(
(
SELECT SUM(effort)
FROM total ep
WHERE ep.name = e.name
AND ep.id < e.id
), 0) AS cp
FROM total e
)
MERGE
INTO #efforts e
USING source s
ON e.id = s.id
WHEN MATCHED AND 40 BETWEEN cp AND ce THEN
UPDATE
SET e.effort = s.effort + 40 - ce
WHEN MATCHED AND cp > 40 THEN
DELETE
WHEN NOT MATCHED BY TARGET THEN
INSERT (id, effort, name)
VALUES (id, effort, name);
SELECT *
FROM #efforts
ORDER BY
name, id
In SQL Server 2005, you'll need two statements (in one transaction):
DECLARE #efforts TABLE (id INT NOT NULL PRIMARY KEY, effort INT NOT NULL, name CHAR(1))
INSERT
INTO #efforts
VALUES (1, 1, 'A')
INSERT
INTO #efforts
VALUES (2, 1, 'A')
INSERT
INTO #efforts
VALUES (3, 8, 'A')
INSERT
INTO #efforts
VALUES (4, 10, 'B')
INSERT
INTO #efforts
VALUES (5, 4, 'B')
INSERT
INTO #efforts
VALUES (6, 1, 'B')
INSERT
INTO #efforts
VALUES (7, 10, 'C')
INSERT
INTO #efforts
VALUES (8, 3, 'C')
INSERT
INTO #efforts
VALUES (9, 30, 'C')
INSERT
INTO #efforts
VALUES (10, 60, 'C')
;WITH total AS
(
SELECT *,
COALESCE(
(
SELECT SUM(effort)
FROM #efforts ep
WHERE ep.name = e.name
AND ep.id <= e.id
), 0) AS cp
FROM #efforts e
)
DELETE
FROM total
WHERE cp > 40
INSERT
INTO #efforts
SELECT (
SELECT MAX(id)
FROM #efforts
) +
ROW_NUMBER() OVER (ORDER BY name),
40 - SUM(effort),
name
FROM #efforts
GROUP BY
name
HAVING SUM(effort) < 40
SELECT *
FROM #efforts
ORDER BY
name, id
This will give you the names that need modify:
SELECT Name, SUM(Effort)
FROM Table
GROUP BY Name
HAVING SUM(Effort) < 40
Select this into a temp table, Add a column for 40 - SUM, then create an insert statement from that. Much better than a cursor.
This will do the first part:
Insert Into dbo.Test (Name, Effort)
Select t.Name, 40 - SUM(t.Effort)
From dbo.Test t
Group By t.Name
Having SUM(t.Effort) < 40
And this will do the second part:
Update a
Set a.Effort = a.Effort - b.AmountToDeduct
From dbo.Test a
Join (
Select t.Name, (40 - SUM(t.Effort)) as 'AmountToDeduct'
From dbo.Test t
Group By t.Name
Having SUM(t.Effort) > 40
)b on a.Name = b.Name
Where a.ID = (Select MAX(c.ID)
From dbo.Test c
Where c.Name = a.Name
)

How to find persons who are absent continuously for n number of days?

Database: MS SQL 2005
Table:
EmployeeNumber | EntryDate | Status
Sample Data:
200 | 3/1/2009 | P
200 | 3/2/2009 | A
200 | 3/3/2009 | A
201 | 3/1/2009 | A
201 | 3/2/2009 | P
Where P is present, A is absent.
I have tried row_number over partion. But it does not generate the sequence which I expect.
For the above data the sequence I expect is
1
1
2
1
1
SELECT EmployeeNumber, EntryDate,Status
ROW_NUMBER() OVER (
PARTITION BY EmployeeNumber, Status
ORDER BY EmployeeNumber,EntryDate ) AS 'RowNumber'
FROM [Attendance]
i'm not sure I follow what you're wanting with the 1 1 2 1 1 sequence, but simply adding an order by to your original query produces that sequence...
SELECT EmployeeNumber,
EntryDate,
Status,
ROW_NUMBER() OVER (PARTITION BY EmployeeNumber, Status ORDER BY EmployeeNumber, EntryDate) AS 'RowNumber'
FROM Attendance
ORDER BY EmployeeNumber, EntryDate
/*
EmployeeNumber EntryDate Status RowNumber
-------------- ----------------------- ------ --------------------
200 2009-03-01 00:00:00 P 1
200 2009-03-02 00:00:00 A 1
200 2009-03-03 00:00:00 A 2
201 2009-03-01 00:00:00 A 1
201 2009-03-02 00:00:00 P 1
(5 row(s) affected)
*/
You should be able to do this with a CTE in SQL 2005. Stealing Lievens data:
DECLARE #Attendance TABLE (EmployeeNumber INTEGER, EntryDate DATETIME, Status VARCHAR(1))
INSERT INTO #Attendance VALUES (200, '03/01/2009', 'P')
INSERT INTO #Attendance VALUES (200, '03/02/2009', 'A')
INSERT INTO #Attendance VALUES (200, '03/03/2009', 'A')
INSERT INTO #Attendance VALUES (200, '03/04/2009', 'A')
INSERT INTO #Attendance VALUES (200, '04/04/2009', 'A')
INSERT INTO #Attendance VALUES (200, '04/05/2009', 'A')
INSERT INTO #Attendance VALUES (201, '03/01/2009', 'A')
INSERT INTO #Attendance VALUES (201, '03/02/2009', 'A')
INSERT INTO #Attendance VALUES (201, '03/03/2009', 'P');
Then use this CTE to extract the sequence:
WITH Dates
(
EntryDate,
EmployeeNumber,
Status,
Days
)
AS
(
SELECT
a.EntryDate,
a.EmployeeNumber,
a.Status,
1
FROM
#Attendance a
WHERE
a.EntryDate = (SELECT MIN(EntryDate) FROM #Attendance)
-- RECURSIVE
UNION ALL
SELECT
a.EntryDate,
a.EmployeeNumber,
a.Status,
CASE WHEN (a.Status = Parent.Status) THEN Parent.Days + 1 ELSE 1 END
FROM
#Attendance a
INNER JOIN
Dates parent
ON
datediff(day, a.EntryDate, DateAdd(day, 1, parent.EntryDate)) = 0
AND
a.EmployeeNumber = parent.EmployeeNumber
)
SELECT * FROM Dates order by EmployeeNumber, EntryDate
Although as a final note the sequence does seem strange to me, depending on your requirements there may be a better way of aggregating the data? Never the less, this will produce the sequence you require
Does this help you?
It doesn't produce the sequence you ask (No idea how to do that) but it does give you the ammount of consecutive days someone has been absent.
DECLARE #Attendance TABLE (EmployeeNumber INTEGER, EntryDate DATETIME, Status VARCHAR(1))
INSERT INTO #Attendance VALUES (200, '03/01/2009', 'P')
INSERT INTO #Attendance VALUES (200, '03/02/2009', 'A')
INSERT INTO #Attendance VALUES (200, '03/03/2009', 'A')
INSERT INTO #Attendance VALUES (200, '03/04/2009', 'A')
INSERT INTO #Attendance VALUES (200, '04/04/2009', 'A')
INSERT INTO #Attendance VALUES (200, '04/05/2009', 'A')
INSERT INTO #Attendance VALUES (201, '03/01/2009', 'A')
INSERT INTO #Attendance VALUES (201, '03/02/2009', 'A')
INSERT INTO #Attendance VALUES (201, '03/03/2009', 'P')
SELECT a1.EmployeeNumber, [Absent] = COUNT(*) + 1
FROM #Attendance a1
INNER JOIN #Attendance a2 ON a1.EntryDate = a2.EntryDate - 1
AND a1.EmployeeNumber = a2.EmployeeNumber
AND a1.Status = a2.Status
GROUP BY a1.EmployeeNumber
You could use recursion, similar to what I have done here. It seems though that your problem is a little simpler, and since SQL Server limits recursion to 99, this might not work for people who are absent a lot. Let me think about this a few minutes.
If you have a row for every single day, go with Lieven's join.