How to select distinct values after left outer join operation - sql-server

I want to select some values from three tables with aggregate function but without duplicates in one of the columns, for example:
select t3.ValueDesc as FeatureType,
count(t2.Strategic) as TotalCount
,t2.RequestID,t1.StoryID --these are not needed, but put for better vision
from tblRequests t2
left outer join (select * from tblAgileMultiDD where Type=18) t3
on t3.FormulaValue = t2.Strategic
left outer join tblAgileStory t1
on t1.Feature = t2.RequestID
where t2.RequestID > 0
and t1.DemoStatus = 1
group by t3.ValueDesc
,t2.RequestID, t1.StoryID --these are not needed but put for better vision
order by t3.ValueDesc
And then it returns me something like this:
FeatureType TotalCount RequestID StoryID
Protect Base 1 311 1629
Protect Base 1 311 1630
Protect Base 1 312 1631
Protect Base 1 312 1637
New Market 1 313 1640
New Market 1 313 1645
And if I comment out lines with ",t2.RequestID, t1.StoryID", it gives me the result:
FeatureType TotalCount
Protect Base 4
New Market 2
So, for each unique combination of RequestID and StoryID it returns new row. How to make it return new row only for each unique RequestID regardless to StoryID?
So I want this query to result like this:
FeatureType TotalCount
Protect Base 2 (for RequestID = 311, 312)
New Market 1 (for RequestID = 313)
Putting word "distinct" at the beginning doesn't take effect on it.
Can you help me with this?

select distinct FeatureType,TotalCount from (
select t3.ValueDesc as FeatureType,
count(t2.Strategic) as TotalCount
,t2.RequestID
-- ,t1.StoryID --these are not needed, but put for better vision
from tblRequests t2
left outer join (select * from tblAgileMultiDD where Type=18) t3
on t3.FormulaValue = t2.Strategic
left outer join tblAgileStory t1
on t1.Feature = t2.RequestID
where t2.RequestID > 0
and t1.DemoStatus = 1
group by t3.ValueDesc
,t2.RequestID
-- , t1.StoryID --these are not needed but put for better vision
) as T
order by t3.ValueDesc
could you try this.

Related

How to Left Inner Join two queries in Sybase?

I have two queries that should be joined together. Here is my query 1:
SELECT
t1.rec_id,
t1.category,
t1.name,
t1.code,
CASE
WHEN t1.name= 'A' THEN SUM(t1.amount)
WHEN t1.name = 'D' THEN SUM(t1.amount)
WHEN t1.name = 'H' THEN SUM(t1.amount)
WHEN t1.name = 'J' THEN SUM(t1.amount)
END AS Amount
FROM Table1 t1
GROUP BY t1.name, t1.rec_id, t1.category, t1.code
Query 1 produce this set of results:
Rec ID Category Name Code Amount
1 1 A MIX 70927.00
1 3 D MIX 19922.00
1 2 H MIX 55104.00
1 4 J MIX 76938.00
Then I have query 2:
SELECT
CASE
WHEN t2.category_id = 1 THEN SUM(t2.sum)
WHEN t2.category_id = 2 THEN SUM(t2.sum)
WHEN t2.category_id = 3 THEN SUM(t2.sum)
WHEN t2.category_id = 4 THEN SUM(t2.sum)
END AS TotalSum
FROM Table2 t2
INNER JOIN Table1 t1
ON t1.amnt_id = t2.amnt_id
AND t2.unique_id = #unique_id
GROUP BY t2.category_id
The result set of query 2 is this:
TotalSum
186013.00
47875.00
12136.00
974602.00
All I need is this result set that combines query 1 and query 2:
Rec ID Category Name Code Amount TotalSum
1 1 A MIX 70927.00 186013.00
1 3 D MIX 19922.00 47875.00
1 2 H MIX 55104.00 12136.00
1 4 J MIX 76938.00 974602.00
As you can see there is connection between table 1 and table 2. That connection is amnt_id. However, I tried doing LEFT INNER JOIN on query 1 and then simply using same logic with case statement to get the total sum for table 2. Unfortunately Sybase version that I use does not support Left Inner Join. I'm wondering if there is other way to join these two queries? Thank you
I wondered if the CASE statement makes sense in the first query because it sums in every row. Are there other values for the name column except A, D, H, J? If not you can change the CASE statement to SUM(t1.amount) AS Amount. Also the GROUP BY in the first query seems dubious to me: you are grouping by the record id column - that means you are not grouping at all but instead return every row. If that is what you really want you can omit the SUM at all and just return the pure amount column.
As far as I understood your problem and your data structure: the values in Table2 are kind of category sums and the values in Table1 are subsets. You would like to see the category sum for every category in Table1 next to the single amounts?
You would typically use a CTE (common table expression, "WITH clause") but ASE doesn't support CTEs, so we have to work with joins. I recreated your tables in my SQL Anywhere database and put together this example. In a nutshell: both queries are subqueries in an outer query and are left joined on the category id:
SELECT *
FROM
(
SELECT
t1.rec_id,
t1.category,
t1.name,
t1.code,
CASE
WHEN t1.name= 'A' THEN SUM(t1.amount)
WHEN t1.name = 'D' THEN SUM(t1.amount)
WHEN t1.name = 'H' THEN SUM(t1.amount)
WHEN t1.name = 'J' THEN SUM(t1.amount)
END AS Amount
FROM Table1 t1
GROUP BY t1.rec_id, t1.name, t1.category, t1.code
) AS t1
LEFT JOIN
(
SELECT category_id, SUM(sum) FROM
table2
GROUP BY category_id
) AS totals(category_id, total_sum)
ON totals.category_id = t1.category;
This query gives me:
Rec ID Category Name Code Amount Category_id total_sum
2 3 D MIX 19922.00 3 47875.00
3 2 H MIX 55104.00 2 12136.00
1 1 A MIX 70927.00 1 186013.00
4 4 J MIX 76938.00 4 974602.00
You surely have to tweak it a bit including your t2.unique_id column (that I don't understand from your queries) but this is a practical way to work around ASE's missing CTE feature.
BTW: it's either an INNER JOIN (only the corresponding records from both tables) or a LEFT (OUTER) JOIN (all from the left, only the corresponding records from the right table) but a LEFT INNER JOIN makes no sense.

SQL query to show good records as well as null records

My query works perfectly well to find records with real values, however, I also need my query to show records with null values. So far my attempts at recreating this query to also show null values has resulted in losing at least 1 of my columns of results so now I'm looking for help.
This is my query so far:
SELECT sq.*, sq.TransactionCountTotal - sq.CompleteTotal as InProcTotal from
(
select
c.CustName,
t.[City],
sum (t.TransactionCount) as TransactionCountTotal
sum (
case
when (
[format] in (23,25,38)
or [format] between 400 and 499
or format between 800 and 899
)
then t.TransactionCount
else 0
end
) as CompleteTotal
FROM [log].[dbo].[TransactionSummary] t
INNER JOIN [log].[dbo].[Customer] c
on t.CustNo = c.CustNo
and t.City = c.City
and t.subno = c.subno
where t.transactiondate between '7/1/16' and '7/11/16'
group by c.CustName,t.City
) sq
This is currently what my query results show:
CustName City InProcTotal TransactionCountTotal Complete Total
Cust 1 City(a) 23 7 30
Cust 2 City(b) 74 2 76
Cust 3 City(c) 54 4 58
This is what I want my query results to show:
CustName City InProcTotal TransactionCountTotal Complete Total
Cust 1 City(a) 23 7 30
Cust 2 City(b) 74 2 76
Cust 3 City(c) 54 4 58
Cust 4 City(d) 0 0 0
Cust 5 City(e) 0 0 0
I suggest you use RIGHT JOIN in the place of INNER JOIN. You should then retain the rows from Customer that don't have matching rows in TransactionSummary.
You may also want to refactor the query like this so you use LEFT JOIN. The next person to work on the query will thank you; LEFT JOIN operations are more common.
FROM [log].[dbo].[Customer] c
LEFT JOIN [log].[dbo].[TransactionSummary] t
on t.CustNo = c.CustNo
and t.City = c.City
jwabsolution, your issue stems from grabbing all transactions instead of all customers. My mind works in this way: you want to select all of the customers & find all transaction states. Therefore, you should be selecting from the customer table. Also, you shouldn't use the INNER JOIN or you will ignore any customers that don't have transactions. Instead, use left join the transactions table. In this manner, you will retrieve all customers (even those with no transactions). Here is a good visual for SQL joins: http://www.codeproject.com/KB/database/Visual_SQL_Joins/Visual_SQL_JOINS_orig.jpg
So your query should look like this:
SELECT sq.*, sq.TransactionCountTotal - sq.CompleteTotal as InProcTotal from
(
select
c.CustName,
t.[City],
sum (t.TransactionCount) as TransactionCountTotal
sum (
case
when (
[format] in (23,25,38)
or [format] between 400 and 499
or format between 800 and 899
)
then t.TransactionCount
else 0
end
) as CompleteTotal
FROM [log].[dbo].[Customer] c
LEFT JOIN [log].[dbo].[TransactionSummary] t
on c.CustNo = t.CustNo
and c.City = t.City
and c.subno = t.subno
where t.transactiondate between '7/1/16' and '7/11/16'
group by c.CustName,t.City
) sq
Fixed it. Needed to use coalesce to get the values to show up properly.
Also added a "where" option if I want to query individual customers
SELECT sq.* ,sq.TransactionCountTotal - sq.CompleteTotal as [InProcTotal]
from
(
select
c.custname
,c.port
,sum(coalesce(t.transactioncount,0)) as TransactionCountTotal
,sum(
case when (
[format]in(23,25,38)
or[format]between 400 and 499
or[format]between 800 and 899)
then t.TransactionCount
else 0
end) as CompleteTotal
from log.dbo.customer c
left join log.dbo.TransactionSummary t
on c.custNo=t.custno
and c.subno=t.subno
and c.city=t.city
and t.transactiondate between '7/1/16' and '7/12/16'
/*where c.custname=''*/
group by c.custname,c.city
) sq

remove duplicates based on column in inner join

This is returning exactly what I want, except some rows need to be removed because the inner join has matched multiple rows when I only want it to match the first match.
select table1.IDa, table1.IDb, table1.name,
table1b.IDa, table1b.IDb, table1b.name
from
(select IDa,IDb,name from mytable) table1
inner join
(select IDa,IDb,name from mytable) table1b
ON
table1.IDa = table1b.IDa
and table1.IDb = table1b.IDb
order By table1.IDa
So I'm getting this:
IDa IDb name IDa IDb name
1 1 bob 1 1 public
1 1 bob 1 1 smith
1 2 sally 1 2 jones
2 1 nancy 2 1 dole
But I want to receive this:
IDa IDb name IDa IDb name
1 1 bob 1 1 public
1 2 sally 1 2 jones
2 1 nancy 2 1 dole
I only want the first match for the IDa+IDb combination returned.
Based on asker's comment
That would be the oldest entry into the database, it would also be the
same as order by IDa,IDb. It would also be the first match seen in the
returned results
Try this query :
select table1.IDa, table1.IDb, table1.name,
table1b.IDa, table1b.IDb, table1b.name
from
(select IDa,IDb,name from mytable) table1
inner join
(select IDa,IDb,name, ROW_NUMBER() OVER( ORDER BY Ida,IDb) as r from mytable ) table1b
ON
table1.IDa = table1b.IDa
and table1.IDb = table1b.IDb
and table1b.r=1
order By table1.IDa
As per your comments this should work, But Smith and Public has same IDa and IDb values hope it is data issue.
;WITH cte
AS (SELECT rn=Row_number()OVER(partition BY table1b.name ORDER BY table1.IDa, table1.IDb),
table1.IDa AS t1_ida,
table1.IDb AS t1_idb,
table1.name AS t1_name,
table1b.IDa AS t2_ida,
table1b.IDb AS t2_idb,
table1b.name AS t2_name
FROM mytable table1
INNER JOIN mytable table1b
ON table1.IDa = table1b.IDa
AND table1.IDb = table1b.IDb)
SELECT *
FROM cte
WHERE rn = 1

Sql LEFT OUTER JOIN with WHERE clause

I have two tables:
Request:
RequestID | Msg
----------------
5 | abc
6 | def
7 | ghi
8 | jkl
RequestStatus:
RequestStatusID | RequestID |StatusID
-------------------------------------
1 5 1
2 8 2
Not every request has a record in RequestStatus
I need all the records from table Request except when StatusID = 2. (requestID=8 should be filter-out)
I am using LEFT OUTER JOIN to recieve the records from table Request but when I am adding Where clause (Where StatusID = 1) of course it does not work.
Move the constraint to your on clause.
select *
from request r
left join requestStatus rs
on r.requestID = rs.requestID
--and status_id = 1
and status_id <> 2
What's happening to you is that the outer join is performed first. Any rows coming from the outer join that don't have matches will have nulls in all the columns. Then your where clause is applied, but since 1 <> null, it's not going to work like you want it to.
EDIT: Changed on clause based on Piyush's comment.
In SQL Server 2017 you can put AND after ON in this way
SELECT * FROM Request
LEFT OUTER JOIN RequestStatus ON Request.RequestID = RequestStatus.RequestID
AND RequestStatus.StatusID <> 2
WHERE Request.RequestID IS NOT NULL
Try this
SELECT *
FROM Request R
LEFT JOIN RequestStatus RS ON R.RequestID = RS.RequestID
WHERE RS.StatusID <> 2 OR RS.RequestID IS NULL
SQL FIDDLE
The answer is pretty simple, use left join but filter on the rows that have StatusId different than 2, like so:
select *
from request r
left join requestStatus rs
on r.requestID = rs.requestID
where rs.StatusId <> 2 or rs.StatusId IS NULL
EDIT: added or rs.StatusId IS NULL additional condition to also include rows in requestStatus table that don't have a match in request table.
You need use an NOT IN Query Statement like this
Select *
From Request
Where RequestID not in
(Select RequestID
From RequestStatus
Where StatusID = 2)
or
Select *
From Request r
left join requestStatus rs on r.requestID = rs.requestID
Where r.RequestID not in
(Select RequestID
From RequestStatus
Where StatusID = 2)
SqlFiddle
This answer assumes you just want the RequestId & Msg from the Request Table where there is not a record in the RequestStatus table with that RequestId and a StatusId of 2.
You won't get extra records for Requests with multiple RequestStatus records using this query either (vs Left join).
Using the not exists clause is faster than Except, Not In, Outer Apply, etc in this article by Aaron Bertrand - Should I use NOT IN, OUTER APPLY, LEFT OUTER JOIN, EXCEPT, or NOT EXISTS?
select r.RequestId, r.Msg
from Request r
where not exists (
select 1
from RequestStatus rs
where rs.StatusId = 2
and rs.RequestId = r.RequestId
)

Select Count Top Inner Join and Where Clause in SQL

This is my Query:
SELECT TOP 3 tablestudentanswer.examid,
tablestudentanswer.studentid,
tablestudentanswer.itemno,
tablestudentanswer.studentanswer,
tablescore.score
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
WHERE tablestudentanswer.examid = 1
AND tablestudentanswer.itemno = 1
ORDER BY tablescore.score ASC
It returns this table:
ExamID StudentID ItemNo StudentAnswer Score
1006 1 1 A 25
1005 1 2 B 30
1004 1 3 A 35
What i want to do is it will return 2 if StudentAnswer='A' and 1 if StudentAnswer='B'
Guys there is nothing wrong with my query on top. What i am asking is what should I add in that query.
I have this which in my mind should return 2 but its an error.
Select COUNT(*) From (
Select Top 3 TableStudentAnswer.ExamID, TableStudentAnswer.StudentID, TableStudentAnswer.ItemNo, TableStudentAnswer.StudentAnswer, TableScore.Score
from TableStudentAnswer
Inner join TableScore on TableStudentAnswer.StudentID=TableScore.StudentID and TableStudentAnswer.ExamID=TableScore.ExamID
where TableStudentAnswer.ExamID=1 and TableStudentAnswer.ItemNo=1
Order By TableScore.Score Asc) where TableStudentAnswer.StudentAnswer = 'A'
It should return:
2
Please help me!
Will this do?
SELECT TOP 3 tablestudentanswer.examid,
tablestudentanswer.studentid,
tablestudentanswer.itemno,
tablestudentanswer.studentanswer,
tablescore.score,
case
when tablestudentanswer.studentanswer = 'A' then 2
when tablestudentanswer.studentanswer = 'B' then 1
else NULL
end as [MyColumn]
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
WHERE tablestudentanswer.examid = 1
AND tablestudentanswer.itemno = 1
ORDER BY tablescore.score ASC
Your question is a bit unclear. Perhaps you want the amount of answers for each?
count(1) over (partition by tablestudentanswer.studentanswer)
This will give you a column with the amount of all the answers with the given studentanswer to each of the rows in the result set. However, note that this could be quite slow. If you can, you're better off using a normal group by.
Do you mean you would like the query to return the number of answers? If so, using COUNT may help.
SELECT tablestudentanswer.studentid,
tablestudentanswer.studentanswer
COUNT(1) AS NumberOfAnswers
FROM tablestudentanswer
INNER JOIN tablescore
ON tablestudentanswer.studentid = tablescore.studentid
AND tablestudentanswer.examid = tablescore.examid
GROUP BY tablestudentanswer.studentid, tablestudentanswer.studentanswer
Please correct me if I am wrong.
By the way, why does your result table doesn't consist of itemno even though you have it in your SELECT statement?

Resources