using combination of select, union and group by - sql

I have two similar tables and a third table with one shared column (sms_status_id) with first and second table. I want to select similar columns from first and second table, union them with third table and then remove repetitive rows.
This is what I've done so far:
select *
from
(
select sms_log_id, atm_id, device_id, error_id, datetime, retries, message
from first_table
join third_table
on sms_log_id = sms_status_id
)
union
(
select sms_id, atm_id, device_id, error_id, datetime, retries, message
from second_table
join third_table
on sms_id = sms_status_id
)
This gives me what I want but the result has repetitive rows. I tried to use
GROUP BY sms_status_id
but I didn't know where should I put it so didn't work out. Any suggestions??
This is w

There is some error in your query.
Please see below code, if this will answer to your question.
#dnoeth is right, UNION always returns distinct rows otherwise use UNION ALL.
mytable1
sms_log_id | Detail
1 a
1 b
1 c
mytable2
sms_id | Detail
1 a
2 b
1 d
1 e
mytable3
id | Status
1 Approve
2 Disapprove
Query:
SELECT t1.sms_log_id as t3_id, t1.Detail, t3.Status
FROM mytable1 as t1
LEFT JOIN mytable3 as t3
ON t1.sms_log_id = t3.id
UNION
SELECT t2.sms_id as t3_id, t2.Detail, t3.Status
FROM mytable2 as t2
LEFT JOIN mytable3 as t3
ON t2.sms_id = t3.id
Result:
t3_id Detail Status
1 a Approve
1 b Approve
1 c Approve
2 b Disapprove
1 d Approve
1 e Approve
you can't see in the result a from mytable2 because it is already in mytable1, and b is printed because it has different status.
Demo

Related

Count when value is in a column in another table

I have two tables
id
val
1
a
1
b
1
c
2
d
2
e
3
f
and
id
1
2
What I want is a count of the number of times an ID appears from the first table ONLY IF if it exists in the second table. How can I do this?
Example output:
id
count
1
3
2
2
3
0
Would you like to show ids that do not exist in the first table?
I made it show according to the ids that exist in the first table, if you want it to show up please comment below
select tb7.id, COUNT(tb6.id) as count
from Table_6 tb6 inner join Table_7 tb7 on tb6.id = tb7.id
group by tb7.id
You can use left outer join and count as follows:
Select t1.id, count(t2.id) as cnt
From table1 t1 left join
table2 t2
on t1.id = t2.id
Group by t1.id;

Querying two tables to filter data using select case

I have two tables
Table 1 looks like this
ID Repeats
-----------
A 1
A 1
A 0
B 2
B 2
C 2
D 1
Table 2 looks like this
ID values
-----------
A 100
B 200
C 100
D 300
Using a view I need a result like this
ID values Repeats
-------------------
A 100 NA
B 200 2
C 100 2
D 300 1
that means, I want unique ID, its values and Repeats. Repeats value should display NA when there are multiple values against single ID and it should display the Repeats value in case there is single value for repeats.
Initially I needed to display the max value of repeats so I tried the following view
ALTER VIEW [dbo].[BookingView1]
AS
SELECT bv.*, bd2.Repeats FROM Table1 bv
JOIN
(
SELECT distinct bd.id, bd.Repeats FROM table2 bd
JOIN
(
SELECT Id, MAX(Repeats) AS MaxRepeatCount
FROM table2
GROUP BY Id
) bd1
ON bd.Id = bd1.Id
AND bd.Repeats = bd1.MaxRepeatCount
) bd2
ON bv.Id = bd2.Id;
and this returns the correct result but when trying to implement the CASE it fails to return unique ID results. Please help!!
One method uses outer apply:
select t2.*, t1.repeats
from table2 t2 outer apply
(select (case when max(repeats) = min(repeats) then max(repeats)
else 'NA'
end) as repeats
from table1 t1
where t1.id = t2.id
) t1;
Two notes:
This assumes that repeats is a string. If it is a number, you need to cast it to a string.
repeats is not null.
For the sake of completeness, I'm including another approach that will work if repeats is NULL. However, Gordon's answer has a much simpler query plan and should be preferred.
Option 1 (Works with NULLs):
SELECT
t1.ID, t2.[Values],
CASE
WHEN COUNT(*) > 1 THEN 'NA'
ELSE CAST(MAX(Repeats) AS VARCHAR(2))
END Repeats
FROM (
SELECT DISTINCT t1.ID, t1.Repeats
FROM #table1 t1
) t1
LEFT OUTER JOIN #table2 t2
ON t1.ID = t2.ID
GROUP BY t1.ID, t2.[Values]
Option 2 (does not contain explicit subqueries, but does not work with NULLs):
SELECT DISTINCT
t1.ID,
t2.[Values],
CASE
WHEN COUNT(t1.Repeats) OVER (PARTITION BY COUNT(DISTINCT t1.Repeats), t1.ID) > 1 THEN 'NA'
ELSE CAST(t1.Repeats AS VARCHAR(2))
END Repeats
FROM #table1 t1
LEFT OUTER JOIN #table2 t2
ON t1.ID = t2.ID
GROUP BY t1.ID, t2.[Values], t1.Repeats
NOTE:
This may not give desired results if table2 has different values for the same ID.

SQL select 1 to many within the same row

I have a table with 1 record, which then ties back to a secondary table which can contain either no match, 1 match, or 2 matches.
I need to fetch the corresponding records and display them within the same row which would be easy using left join if I just had 1 or no matches to tie back, however, because I can get 2 matches, it produces 2 records.
Example with 1 match:
Select T1.ID, T1.Person1, T2.Owner
From T1
Left Join T2
ON T1.ID = T2.MatchID
Output
ID Person1 Owner1
----------------------
1 John Frank
Example with 2 match:
Select T1.ID, T1.Person1, T2.Owner
From T1
Left Join T2
ON T1.ID = T2.MatchID
Output
ID Person1 Owner
----------------------
1 John Frank
1 John Peter
Is there a way I can formulate my select so that my output would reflect the following When I have 2 matches:
ID Person1 Owner1 Owner2
-------------------------------
1 John Frank Peter
I explored Oracle Pivots a bit, however couldn't find a way to make this work. Also explored the possibility of using left join on the same table twice using MIN() and MAX() when fetching the matches, however I can only see myself resorting this as a "no other option" scenario.
Any suggestions?
** EDIT **
#ughai - Using CTE does address the issue to some extent, however when attempting to retrieve all of the records, the details derived from this common table isn't showing any records on the LEFT JOIN unless I specify the "MatchID" (CASE_MBR_KEY) value, meaning by removing the "where" clause, my outer joins produce no records, even though the CASE_MBR_KEY values are there in the CTE data.
WITH CTE AS
(
SELECT TEMP.BEAS_KEY,
TEMP.CASE_MBR_KEY,
TEMP.FULLNAME,
TEMP.BIRTHDT,
TEMP.LINE1,
TEMP.LINE2,
TEMP.LINE3,
TEMP.CITY,
TEMP.STATE,
TEMP.POSTCD,
ROW_NUMBER()
OVER(ORDER BY TEMP.BEAS_KEY) R
FROM TMP_BEN_ASSIGNEES TEMP
--WHERE TEMP.CASE_MBR_KEY = 4117398
)
The reason for this is because the ROW_NUMBER value, given the amount of records won't necessarily be 1 or 2, so I attempted the following, but getting ORA-01799: a column may not be outer-joined to a subquery
--// BEN ASSIGNEE 1
LEFT JOIN CTE BASS1
ON BASS1.CASE_MBR_KEY = C.CASE_MBR_KEY
AND BASS1.R IN (SELECT min(R) FROM CTE A WHERE A.CASE_MBR_KEY = C.CASE_MBR_KEY)
--// END BA1
--// BEN ASSIGNEE 2
LEFT JOIN CTE BASS2
ON BASS2.CASE_MBR_KEY = C.CASE_MBR_KEY
AND BASS2.R IN (SELECT MAX(R) FROM CTE B WHERE B.CASE_MBR_KEY = C.CASE_MBR_KEY)
--// END BA2
** EDIT 2 **
Fixed the above issue by moving the Row number clause to the "Where" portion of the query instead of within the JOIN clause. Seems to work now.
You can use CTE with ROW_NUMBER() with 2 LEFT JOIN OR with PIVOT like this.
SQL Fiddle
Query with Multiple Left Joins
WITH CTE as
(
SELECT MatchID,Owner,ROW_NUMBER()OVER(ORDER BY Owner) r FROM t2
)
select T1.ID, T1.Person, t2.Owner as Owner1, t3.Owner as Owner2
FROM T1
LEFT JOIN CTE T2
ON T1.ID = T2.MatchID AND T2.r = 1
LEFT JOIN CTE T3
ON T1.id = T3.MatchID AND T3.r = 2;
Query with PIVOT
WITH CTE as
(
SELECT MatchID,Owner,ROW_NUMBER()OVER(ORDER BY Owner) R FROM t2
)
SELECT ID, Person,O1,O2
FROM T1
LEFT JOIN CTE T2
ON T1.ID = T2.MatchID
PIVOT(MAX(Owner) FOR R IN (1 as O1,2 as O2));
Output
ID PERSON OWNER1 OWNER2
1 John Maxwell Peter
If you know there are at most two matches, you can also use aggregation:
Select T1.ID, T1.Person1,
MIN(T2.Owner) as Owner1,
(CASE WHEN MIN(t2.Owner) <> MAX(t2.Owner) THEN MAX(t2.Owner) END) as Owner2
From T1 Left Join
T2
on T1.ID = T2.MatchID
Group By t1.ID, t1.Person1;

Fetching records from multiple tables logically with SQLServer

I am having a problem with fetching the records.
Finding it too difficult to get tis but though I am trying.
Please help me regarding this::
Actually My requirement is to get that records from table1 whose sender and reciever userid =5 and to that corresponding records if the pkId of the selected result exists in
table2 as fkid and have to check userId again in table2 with Isdeleted=false
I want to get records from table1 only where sender=5 or receiver=5
and then I need to check the pkId of table1 if it exists in
table2 as fkId then idDeleted should be false but
while checking from table2, I am having a condition to search only where
table2.userid =5
I have tried this but not working
select distinct pkId , Message, data from table1
left outer join table2 on
table2.fkId =table1.pkMessageId
where ((table1.sender ='5' or table1.receiver='5' )
or table2.userid='5') and table2.isDeleted=0
table1
pkId sender receiver Message data date
1 2 5 M1 D1 blah_Blah
2 2 5 M2 D2 blah_Blah
3 5 7 M3 D3 blah_Blah
4 5 2 M4 D4 blah_Blah
5 5 2 M5 D4 blah_Blah
table2
Id fkId userid isDeleted
1 1 5 true
2 1 5 false
3 2 5 false
4 2 2 false
5 3 2 false
6 4 2 true
7 1 2 true
8 2 2 false
Try this:
Select distinct pkId , Message, data from table1
Where (sender = 5 Or receiver = 5) and pkId in
(
Select fkid From table
Where isDeleted=0
)
You should be able to avoid the distinct with an EXISTS statement:
select pkId, Message, data
from table1
where exists ( select 1
from table2
where table2.fkId = table1.pkMessageId
and table2.isDeleted=0
and table2.userid='5' )
and (sender ='5' or receiver='5' )
This is assuming a few things though:
table2.fkId = table1.pkMessageId is the right way of joining the two tables (pkMessageId is lacking in your example data)
You do not want table1.sender ='5' or table1.receiver='5' or table2.userid='5'
If you want OR, OR, OR, this could be what you are looking for:
select pkId, Message, data
from table1
where exists ( select 1
from table2
where table2.fkId = table1.pkMessageId
and table2.isDeleted = 0
and table2.userid='5' )
or sender ='5'
or receiver='5'
I think your issue is you are using the same table for sender and receiver you need to join to the same table twice...
SELECT * FROM Table1
INNER JOIN Table2 Sender
ON Table1.Sender = Sender.userid
INNER JOIN Table2 Receiver
ON Table1.Receiver = Receiver.userid
WHERE
Receiver.isDeleted = 0
AND
Sender.isDeleted = 0
AND
(
Receiver.userid = 5
OR
Sender.userid = 5
)
from your description what you are saying is
SELECT
T1.*
FROM
Table1 T1
INNER JOIN
Table2 T2
ON
T1.pkId = T2.fkId
WHERE
(T1.Sender = 5 OR T1.Receiver = 5)
AND
T2.isDeleted = 0
AND
T2.userid = 5
I will go step by step through your new version of your requirements:
Actually My requirement is to get that records from table1 whose sender and reciever userid =5 and to that corresponding records if the pkId of the selected result exists in table2 as fkid and have to check userId again in table2 with Isdeleted=false
I'll take this step by step.
records from table1 whose sender and reciever userid =5
That is easy, in your example data, there are no such records.
Now, I will assume you mean
records from table1 whose sender OR reciever userid =5
So that leads to a first part of our query:
SELECT *
FROM Table1 t1
WHERE t1.sender = 5
OR t1.receiver = 5
Then, we go further:
and to that corresponding records if the pkId of the selected result exists in table2 as fkid
So, we LEFT JOIN table 2 on matching keys:
SELECT *
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.pkID = t2.fkId
WHERE t1.sender = 5
OR t1.receiver = 5
Almost there:
and have to check userId again in table2 with Isdeleted=false
I am assuming you want to only add the extra data from table2 if this condition is met, but is the condition is not met, you still want the data from table1. To acheive this, I will put this condition in the JOIN condition, rather than in th eWHERE clause. In the where clause, if the condition is not met, the whole record will be removed, not just the data from table2.
SELECT *
FROM Table1 t1
LEFT JOIN Table2 t2 ON t1.pkID = t2.fkId
AND t2.IsDeleted = 0
WHERE t1.sender = 5
OR t1.receiver = 5
Now, this looks a lot like what you already had created, but I would not know why it would not give you the desired result. So if this does not give you the desired or expected result, please tell us what is going wrong :)
--Try Like This..
SELECT * FROM Table1
Where ( Sender = 5
OR Reciever = 5 )
AND PKID in ( Select FKID From Table2 Where IsDeleted = 0)
-- It would be easy if u can provide the table structures also...

SQL Server - Setting multiple columns from another table

I have two tables.
Table 1
ID Code1 Code2 Code3
10 1.1 1.2 1.3
Table 2
Code Group Category
1.1 a cat1
1.2 b cat1
1.3 c cat2
1.4 d cat3
Now I need to get the outputs in two different forms from these two tables tables
Output 1
ID Group1 Group2 Group3
10 a b c
Output 2
ID cat1 cat2 cat3
10 1 1 0
Here the cat1, cat2, cat3 columns are Boolean in nature since the table 1 did not have any code corresponding to cat3 so the value for this is 0.
I was thinking of doing this with case statements but there are about 1000 codes mapped to about 50 categories. Is their a way to do this? I am struggling to come up with a query for this.
First off, I strongly suggest you look into an alternative. This will get messy very fast, as you're essentially treating rows as columns. It doesn't help much that Table1 is already denormalized - though if it really only has 3 columns, it's not that big of a deal to normalize it again.:
CREATE VIEW v_Table1 AS
SELECT Id, Code1 as Code FROM Table1
UNION SELECT Id, Code2 as Code FROM Table1
UNION SELECT Id, Code3 as Code FROM Table1
If we take you second query, it appears you want all possible combinations of ID and Category, and a boolean of whether that combination appears in Table2 (using Code to get back to ID in Table1).
Since there doesn't appear to be a canonical list of ID and Category, we'll generate it:
CREATE VIEW v_AllCategories AS
SELECT DISTINCT ID, Category FROM v_Table1 CROSS JOIN Table2
Getting the list of represented ID and Category is pretty straightforward:
CREATE VIEW v_ReportedCategories AS
SELECT DISTINCT ID, Category FROM Table2
JOIN v_Table1 ON Table2.Code = v_Table1.Code
Put those together, and we can then get the bool to tell us which exists:
CREATE VIEW v_CategoryReports AS
SELECT
T1.ID, T1.Category, CASE WHEN T2.ID IS NULL THEN 0 ELSE 1 END as Reported
FROM v_AllCategories as T1
LEFT OUTER JOIN v_ReportedCategories as T2 ON
T1.ID = T2.ID
AND T1.Category = T2.Category
That gets you your answer in a normalized form:
ID | Category | Reported
10 | cat1 | 1
10 | cat2 | 1
10 | cat3 | 0
From there, you'd need to do a PIVOT to get your Category values as columns:
SELECT
ID,
cat1,
cat2,
cat3
FROM v_CategoryReports
PIVOT (
MAX([Reported]) FOR Category IN ([cat1], [cat2], [cat3])
) p
Since you mentioned over 50 'Categories', I'll assume they're not really 'cat1' - 'cat50'. In which case, you'll need to code gen the pivot operation.
SqlFiddle with a self-contained example.
These answers assume that all 3 codes are available in table 2. If not, then you should use OUTER joins instead of INNER.
Output 1 can be achieved like this:
select t1.ID,
cd1.Group as Group1,
cd2.Group as Group2,
cd3.Group as Group3
from table1 t1
inner join table2 cd1
on t1.Code1 = cd1.Code
inner join table2 cd2
on t1.Code2 = cd2.Code
inner join table2 cd3
on t1.Code3 = cd3.Code
Output 2 is trickier. Since you want a column for every row in Table2, you could write SQL that writes SQL.
Basically start with this base statement:
select t1.ID,
//THE BELOW WILL BE GENERATED ONCE PER ROW
Case when cd1.Category = '' OR
cd2.Category = '' OR
cd3.Category = '' then convert(bit,1) else 0 end as '',
//END GENERATED CODE
from table1 t1
inner join table2 cd1
on t1.Code1 = cd1.Code
inner join table2 cd2
on t1.Code2 = cd2.Code
inner join table2 cd3
on t1.Code3 = cd3.Code
then you can generate the code in the middle like this:
select distinct 'Case when cd1.Category = '''+t2.Category+''' OR
cd2.Category = '''+t2.Category+''' OR
cd3.Category = '''+t2.Category+''' then convert(bit,1) else 0 end as ['+t2.Category+'],'
from table2 t2
Paste those results into the original SQL statement (strip off the trailing comma) and you should be good to go.
We can use the Pivot feature and build the query dynamically. Some what like below:
Query 1
Select * from
(SELECT Id, Code, GroupCode
FROM Table2 join Table1
ON Table1.Code1 = Table2.Code
OR Table1.Code2 = Table2.Code
OR Table1.Code3 = Table2.Code
) ps
PIVOT
(
Max (GroupCode)
FOR Code IN
( [1.1], [1.2], [1.3])
) AS Result
Query 2
Select * from
(SELECT Id, GroupCode, Category
FROM Table2 join Table1
ON Table1.Code1 = Table2.Code
OR Table1.Code2 = Table2.Code
OR Table1.Code3 = Table2.Code
) ps
PIVOT
(
Count (GroupCode)
FOR Category IN
( [cat1], [cat2], [cat3])
) AS Result
Unfortunately your stuck with a bad design for Table1. A better approach would have been to have 3 rows for ID 10.
But, given your current design, your query will look something like this:
SELECT ID, G1.Group Group1, G2.Group Group2, G3.Group Group3
FROM Table1 T1
INNER JOIN Table2 G1 ON T1.Code1 = G1.Code
INNER JOIN Table2 G2 ON T1.Code2 = G2.Code
INNER JOIN Table2 G3 ON T1.Code3 = G3.Code
and
SELECT ID, G1.Category Cat1, G2.Category Cat2, G3.Category Cat3
FROM Table1 T1
INNER JOIN Table2 G1 ON T1.Code1 = G1.Code
INNER JOIN Table2 G2 ON T1.Code2 = G2.Code
INNER JOIN Table2 G3 ON T1.Code3 = G3.Code
The PIVOT and CROSS APPLY keywords within MSSQL would help you out. Though it's not exactly clear what you are trying to accomplish. CROSS APPLY for performing a join on a correlated subquery and displaying different output for each join, and PIVOT for doing a crosstab on your data.
For table 1 it might be easier if you mash it together into a more normalized style.
WITH cteTab1 (Id, Code) AS
(
SELECT Id, Code1 FROM Table1
UNION ALL
SELECT Id, Code2 FROM Table1
UNION ALL
SELECT Id, Code3 FROM Table1)
SELECT *
FROM Table2 INNER JOIN cteTab1 ON Table2.Code = cteTab1.Code