Sum two columns in SQL and optimise SQL query - sql-server

I have the following query which returns two columns, I want to sum both columns and create the third column through summing the two.
Is there any way I can recreate the below query by removing the subquery? Any way I can achieve the same through joins?
SELECT
IIF(c2.isdeleted = 1 OR c2.approved = 0, 0, 1) AS Contentcount,
(SELECT COUNT(c1.content)
FROM comments c1
WHERE c1.parentcommentid = c2.id
AND c1.isdeleted = 0
AND c1.approved = 1) ChildContentcount --Anyway to remove the subquery
FROM
comments c2
WHERE
c2.discussionid = '257943'
AND c2.parentcommentid IS NULL
ORDER BY
c2.pinned DESC,
c2.createddate
Sample data:
+----------+--------------+
| content | childcontent |
+----------+--------------+
| 1 | 8 |
| 0 | 0 |
| 1 | 3 |
+----------+--------------+
Expected output:
+----------+----------------+---------+
| content | childcontent | sumdata |
+----------+----------------+---------+
| 1 | 8 | 9 |
| 0 | 0 | 0 |
| 1 | 3 | 4 |
| 1 | 8 | 9 |
+----------+----------------+---------+

You can use CROSS APPLY or OUTER APPLY instead of a correlated subquery.
Then you can re-use the values.
select c.pinned, c.createddate
, c.discussionid
, ca1.content
, ca2.childcontent
, (ca1.content + ca2.childcontent) AS sumdata
FROM comments c
CROSS APPLY
(
SELECT CASE
WHEN c.isdeleted = 1 OR c.approved = 0 THEN 0
ELSE 1
END AS content
) ca1
CROSS APPLY
(
SELECT COUNT(c2.content) AS childcontent
FROM comments c2
WHERE c2.parentcommentid = c.id
AND c2.isdeleted = 0
AND c2.approved = 1
) ca2
WHERE c.discussionid = '257943'
AND c.parentcommentid IS NULL
ORDER BY
c.pinned DESC,
c.createddate;

Subquery and sum the columns :
select tbl.* , Contentcount+ChildContentcount third_sum from
(
select IIF(c2.isdeleted = 1 OR c2.approved = 0, 0, 1) AS Contentcount,
(SELECT COUNT(c1.content)
FROM comments c1
WHERE c1.parentcommentid = c2.id
AND c1.isdeleted = 0
AND c1.approved = 1) ChildContentcount
FROM
comments c2
WHERE
c2.discussionid = '257943'
AND c2.parentcommentid IS NULL ) tbl
If you supply sql fiddle, we can try to create it alternative ways

Related

How to get value conditionally from another row in sub table

Select * from LoanAccount main INNER JOIN LoanSubAccount sub
WHERE main.LoanAccountID = sub.LoanAccountID
AND sub.LoanStatus = 4
My objective is to retrieve rows with LoanStatus = 4 but replace the amount with records with LoanStatus = 2.
End result expected to be
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY LoanAccountID, LoanStatus
ORDER BY LoanSubAccountID) rn
FROM LoanSubAccount
)
SELECT t1.LoanSubAccountID,
t1.LoanAccountID,
t1.LoanStatus,
t1.CommodityType,
t2.Amount
FROM cte t1
INNER JOIN cte t2
ON t1.rn = t2.rn AND
t1.LoanStatus > t2.LoanStatus
Rather than giving a verbose explanation, I would rather show a table representing what the above CTE would look like:
rn | LoanSubAccountID | LoanAccountID | LoanStatus | CommodityType | Amount
1 | 1 | 1 | 2 | 1 | 100
2 | 2 | 1 | 2 | 2 | 200
1 | 3 | 1 | 4 | 3 | 150
2 | 4 | 1 | 4 | 4 | 150
If I read your requirement correctly, you want to connect rows having the same row number from the two different loan statuses. The join query I gave above does this.

Select records having one of two possibilities in sql server query

Thank you in advance for your help, here I go:
I have a table like this:
|Content | Syndication_type | Syndication_publication|
------------------------------------------------------
| A | 1 | 1 |
| A | 2 | 1 |
| B | 2 | 1 |
| C | 1 | 0 |
| D | 1 | 0 |
| D | 2 | 1 |
| E | 2 | 1 |
| F | 1 | 1 |
I need to get Content which has only one syndication_type with syndication_publication = 1.
For example, if I choose syndication_type = 2, I have to obtain Content = B, Content = D, and Content = E, since they have only syndication_publication = 1 for syndication_type = 2.
Content = 2 is not the case since it has both Syndication_type = 1 and Syndication_type = 2 with Syndication_publication = 1, whereas, Content = D is ok, since it has only Syndication_publication = 2 with Syndication_publication = 1.
I hope I explained my purpose... :)
Thank you very much for your help.
Try:
SELECT Content
FROM yourtable
WHERE Syndication_type = 1 -- your conditions
GROUP BY Content, Syndication_type
HAVING COUNT(DISTINCT Syndication_publication) = 1
You can use NOT EXISTS
SELECT [Content]
FROM MyTable mt
WHERE [Syndication_type] = 2
AND NOT EXISTS (
SELECT 1
FROM MyTable mt2
WHERE mt2.[Content] = mt.[Content]
AND mt2.Syndication_publication = 1
AND mt2.Syndication_type <> mt.Syndication_type
)

SQL Query : Obtaining Rows where Columns A in Table A = Column A in Table B

I am probably being completely thick here.
I have a tables with a list of reason codes in, the 'access' to these reasons is limited by whether or not columns have 1 or 0 in.
ID | Category | Reason | Brand1 | Brand2 | Brand3
1 | Test | That | 1 | 1 | 0
2 | Test2 | This | 0 | 1 | 0
3 | Test3 | Mine | 0 | 0 | 1
The user table
Username | Brand1 | Brand2 | Brand3
User1 | 1 | 1 | 0
Basically User1 should only be able to see the reasons whereby they have a 1 in the Users Table in the Brand Columns they have a 1 in the Brand column in the Reasons Table.
User1 should only be able to see the reason ID 1 & 2 because they have a 1 in Brand1 and 1 in Brand2 in the User Table.
I only wish to return the rows from the Reasons Table, whereby the User in the Users Table has a match 1 in the corresponding 'Brand' column.
i.e.
User 1 should return
ID | Category | Reason | Brand1 | Brand2 | Brand3
1 | Test | That | 1 | 1 | 0
2 | Test2 | This | 0 | 1 | 0
How would I get this in SQL, I tried writing it with OR's in the WHERE statement but it was always returning every row. There are 7 Brand columns in total.
I think I'm overlooking something simple here from over complicating the matter.
Cheers
If you post your SQL then I could get something closer.
Otherwise:
SELECT *
FROM Reasons AS R
INNER JOIN UserTable AS U
ON (U.Brand1 = 1 AND U.Brand1 = R.Brand1)
OR (U.Brand2 = 1 AND U.Brand2 = R.Brand2)
OR (U.Brand3 = 1 AND U.Brand3 = R.Brand3);
This will get only the rows where the user has a brand that the reason also has. Technically you could change the U.Brand# = R.Brand# to just R.Brand# = 1, but that might be hard for others to read later.
SELECT *
FROM Reasons AS R
INNER JOIN UserTable AS U
ON (U.Brand1 = 1 AND R.Brand1 = 1)
OR (U.Brand2 = 1 AND R.Brand2 = 1)
OR (U.Brand3 = 1 AND R.Brand3 = 1);
This would probably be faster than the second query, but not enough to notice in most situations.
If I Understood Correctly try below script
SELECT t.ID , t.Category , t.Reason , t.Brand1 , t.Brand2 , t.Brand3
FROM TableName t,UserTable U
WHERE (t.Brand1 =U.Brand1 AND t.Brand1 = 1 ) OR
(t.Brand2 = U.Brand2 AND t.Brand2 = 1 ) OR
(t.Brand3 =U.Brand3 AND t.Brand3 = 1 )
SELECT
rt.*
FROM ReasonTable AS rt
INNER JOIN UserTable AS ut ON (rt.Id = 1 AND ut.Brand1 = 1)
OR (rt.Id = 2 AND ut.Brand2 = 1) OR (rt.Id = 3 AND ut.Brand2 = 1)
WHERE ut.UserId = #userId

Update table based on identical values in a column on same table

I have a table like this
InsuredID | EmployeeNumber | MemberTypeID | LinkedMemberID
----------------------------------------------------------------
1001012 | 39018 | 102 | 0
1001061 | 39018 | 100 | 0
1001147 | 39019 | 102 | 0
1001196 | 39019 | 100 | 0
I need to update LinkedMemberID in this table to value of InsuredID of the with same EmployeeNumber and MemberTypeID = 100 for all MemberTypes which are not 100.
LinkedMemberID of MemberTypeID = 100 will remain 0.
After update, the table should look like
InsuredID | EmployeeNumber | MemberTypeID | LinkedMemberID
----------------------------------------------------------------
1001012 | 39018 | 102 | 1001061
1001061 | 39018 | 100 | 0
1001147 | 39019 | 102 | 1001196
1001196 | 39019 | 100 | 0
I have tried various SQLupdate statements but can't figure out how to do this. I am using SQL Server 2008. Please help.
If I understand correctly, you want an update statement and you can do what you want with a join:
update toupdate
set LinkedMemberID = t.InsuredID
from table toupdate join
table t
on toupdate.EmployeeNumber = t.EmployeeNumber and
t.MemberTypeID = 100
where toupdate.MemberTypeID <> 100;
You can use the APPLY function:
SELECT
t.InsuredID,
EmployeeNumber,
MemberTypeID,
LinkedMemberID = CASE WHEN t.MemberTypeID = 100 THEN LinkedMemberID ELSE x.InsuredID END
FROM tbl t
OUTER APPLY (
SELECT InsuredID
FROM tbl
WHERE EmployeeNumber = t.EmployeeNumber
AND MemberTypeID = 100
)x
SQL Fiddle
Transforming it to an UPDATE statement:
UPDATE t
SET t.LinkedMemberID = x.InsuredID
FROM tbl t
OUTER APPLY (
SELECT TOP 1 InsuredID
FROM tbl
WHERE EmployeeNumber = t.EmployeeNumber
AND MemberTypeID = 100
ORDER BY InsuredID DESC
)x
WHERE t.MemberTypeID <> 100
SQL Fiddle
You can try this:
UPDATE Your_Table
SET LinkedMemberID = (SELECT TOP 1 T.InsuredID
FROM Your_Table T
WHERE T.EmployeeNumber = Your_Table.EmployeeNumber
AND T.MemberTypeID = 100)
WHERE MemberTypeID <> 100

Bayesian ratings in postgresql

I have the following table in my db
Name | total_stars | total_reviews
Item A 27 7
Item B 36 9
Item C 27 7
Item D 30 6
Item E 0 0
Item F 0 0
Item F 15 3
I was looking at this article and was trying to implement bayesian rankings in postgresql database.
The formula given for the rank is
br = ( (avg_num_votes * avg_rating) + (this_num_votes * this_rating) ) /
(avg_num_votes + this_num_votes)
where:
avg_num_votes: The average number of votes of all items that have num_votes>0
avg_rating: The average rating of each item (again, of those that have num_votes>0)
this_num_votes: number of votes for this item
this_rating: the rating of this item
This is the query I came up with, but it is not working:
with avg_num_votes as (
select AVG(total_reviews)
from business
where total_reviews != 0),
avg_rating as (
select AVG(total_stars/total_reviews)
from business
where total_reviews != 0)
select * from business
order by ((avg_num_votes * avg_rating) + (total_stars)) / (avg_num_votes + total_reviews);
I am getting: ERROR: column "avg_num_votes" does not exist
Operator WITH in Postgres is only used to create additional working queries to be used in main query.
This query is using sub-selects in FROM clause instead and works as you expect:
SELECT business.* FROM business,
(SELECT avg(total_reviews) AS v
FROM business
WHERE total_reviews != 0
) AS avg_num_votes,
(SELECT avg(total_stars/total_reviews) AS v
FROM business
WHERE total_reviews != 0
) AS avg_rating
ORDER BY ((avg_num_votes.v * avg_rating.v) + (total_stars)) / (avg_num_votes.v + total_reviews)
EDIT: Actually, using WITH is also possible, but does not seem to be shorter compared to first form. Also, it is less portable - first solution will work on MySQL, but this will not:
WITH
avg_num_votes AS (
SELECT avg(total_reviews) AS v
FROM business
WHERE total_reviews != 0
),
avg_rating AS (
SELECT avg(total_stars/total_reviews) AS v
FROM business
WHERE total_reviews != 0
)
SELECT business.*
FROM business, avg_num_votes, avg_rating
ORDER BY ((avg_num_votes.v * avg_rating.v) + (total_stars)) / (avg_num_votes.v + total_reviews)
SQL Fiddle
with av as (
select avg(total_reviews) avg_num_votes
from business
where total_reviews > 0
), ar as (
select name, avg(total_stars * 1.0 / total_reviews) avg_rating
from business
where total_reviews > 0
group by name
)
select b.*, avg_rating, avg_num_votes,
(avg_num_votes * avg_rating + total_stars)
/
(avg_num_votes + total_reviews) br
from
business b
left join
ar on ar.name = b.name
inner join
av on true
order by br, b.name
;
name | total_stars | total_reviews | avg_rating | avg_num_votes | br
--------+-------------+---------------+--------------------+--------------------+------------------------------------
Item A | 27 | 7 | 3.8571428571428571 | 6.4000000000000000 | 3.85714285714285712238805970149254
Item C | 27 | 7 | 3.8571428571428571 | 6.4000000000000000 | 3.85714285714285712238805970149254
Item B | 36 | 9 | 4.0000000000000000 | 6.4000000000000000 | 4.00000000000000000000000000000000
Item D | 30 | 6 | 5.0000000000000000 | 6.4000000000000000 | 5.00000000000000000000000000000000
Item F | 0 | 0 | 5.0000000000000000 | 6.4000000000000000 | 5.00000000000000000000000000000000
Item F | 15 | 3 | 5.0000000000000000 | 6.4000000000000000 | 5.00000000000000000000000000000000
Item E | 0 | 0 | | 6.4000000000000000 |

Resources