SQL Server Group By Combine Pivot Table Approaches - sql-server

I have tables which designed like survey questions.
Answer table store like below. One answer related with three questions. QuestionId 1 and QuestionId 2 store Yes or No answers. If QuestionId 1 and QuestionId 2 both of them answer Yes, I want QuesiontId 3 to sum count with column value otherwise alias should be Others. Which approach is better , could u please help :)
AnswerId | QuestionId | QuestionOptionText
---------+------------+-------------------
1 | 1 | No
1 | 2 | No
1 | 3 | 0
--------------------------------------
2 | 1 | Yes
2 | 2 | No
2 | 3 | 0
--------------------------------------
3 | 1 | No
3 | 2 | Yes
3 | 3 | 1-10
--------------------------------------
4 | 1 | Yes
4 | 2 | Yes
4 | 3 | 1-10
--------------------------------------
5 | 1 | Yes
5 | 2 | Yes
5 | 3 | 11-20
Result should be like that
1-10 | 11-20 | Other
-----+-------+-------
1 | 1 | 3

I think you need a self join of the three types of question, and then the summing logic is easier. A different table structure would probably have simplified things. Normally you would have each question in a separate column.
select
sum(iif(ans1 = 'Yes' and ans2 = 'Yes' and ans3 = '1-10',1,0)) as '1-10',
sum(iif(ans1 = 'Yes' and ans2 = 'Yes' and ans3 = '11-20',1,0)) as '11-20',
sum(iif(ans1 <> 'Yes' or ans2 = 'Yes',1,0)) as 'other'
from (
select QuestionOptionText as ans1 from t where QuestionId = 1
) as a1
inner join (
select QuestionOptionText as ans2 from t where QuestionId = 2
) as a2
on a1.AnswerId = a2.AnswerId
inner join (
select QuestionOptionText as ans3 from t where QuestionId = 3
) as a3
on a1.AnswerId = a3.AnswerId

Is this what you want?
select sum(case when QuestionOptionText = '1-10' then 1 else 0 end),
sum(case when QuestionOptionText = '11-20' then 1 else 0 end),
sum(case when QuestionOptionText not in ('1-10', '11-20') then 1 else 0 end)
from t
where QuestionId = 3;

You could do this with two levels of aggregation. First compute the outcome of each series of answerId, the pivot the resultset:
select
sum(case when res = '1-10' then 1 else 0 end) res_1_10,
sum(case when res = '11-20' then 1 else 0 end) res_1_20,
sum(case when res = 'Other' then 1 else 0 end) res_other
from (
select
case when max(case when QuestionId in (1, 2) then QuestionOptionText end) = 'Yes'
then max(case when QuestionId = 3 then QuestionOptionText end)
else 'Other'
end res
from mytable
group by answerId
) t

Related

Sql rows count with grouping

Hello I have two tables as below
tblContactType
typeId typeName active
1 Email 1
2 Phone 1
3 Address 1
4 Fax 1
tblContact
id IdName typeId groupId
100 test 1 1
101 test2 1 1
102 test3 1 2
103 test4 2 2
104 test5 2 3
105 test6 3 3
Want the results to be with column names as typeName count and grouped by group id. Results should be total number of types associated to a group,which are associated to a contact.
GroupId EmailCount PhoneCount AddressCount FaxCount
1 2 0 0 0
2 1 1 0 0
3 0 1 1 0
You can group by and pivot as below:
Select * from (
Select t.groupid, tct.typename, t.id from tblContact t
inner join tblContactType tct
on t.typeid = tct.typeid
) a
pivot (count(a.id) for typename in ([Email],[Phone],[Address],[Fax]) ) p
For dynamic list of columns you can use dynamic query as below:
declare #cols1 varchar(max)
declare #query nvarchar(max)
Select #cols1 = stuff((Select distinct ','+QuoteName(typename) from tblContactType for xml path('')),1,1,'')
Set #query = ' Select * from (
Select t.groupid, tct.typename, t.id from tblContact t
inner join tblContactType tct
on t.typeid = tct.typeid
) a
pivot (count(a.id) for typename in (' + #cols1 + ') ) p '
Select #query --Check the generated query is good and then execute below
--exec sp_executesql #query
Output as below:
+---------+---------+-------+-----+-------+
| groupid | Address | Email | Fax | Phone |
+---------+---------+-------+-----+-------+
| 1 | 0 | 2 | 0 | 0 |
| 2 | 0 | 1 | 0 | 1 |
| 3 | 1 | 0 | 0 | 1 |
+---------+---------+-------+-----+-------+
Here is another solution.
SELECT groupId,
SUM(CASE WHEN c.typeId = 1 THEN 1 ELSE 0 END) 'EmailCount',
SUM(CASE WHEN c.typeId = 2 THEN 1 ELSE 0 END) 'PhoneCount',
SUM(CASE WHEN c.typeId = 3 THEN 1 ELSE 0 END) 'AddressCount',
SUM(CASE WHEN c.typeId = 4 THEN 1 ELSE 0 END) 'FaxCount'
FROM tblContact c
JOIN tblContactType ct ON c.typeId = ct.typeId
GROUP BY groupId
Results
-------------------------------------------------------------
groupId | EmailCount | PhoneCount | AddressCount | FaxCount
-------------------------------------------------------------
1 | 2 | 0 | 0 | 0
2 | 1 | 1 | 0 | 0
3 | 0 | 1 | 1 | 0
-------------------------------------------------------------

Select data with only specific factors from many

I'm using T-SQL
I have a table: user session with platform source.
I need to get users who used only "Android" and "Desktop".
Data Example:
ID | Source | DateTime
1 | Android | 2016-06-01
1 | Desktop | 2016-06-01
2 | Android | 2016-06-02
3 | iOS | 2016-06-01
3 | Desktop | 2016-06-02
3 | Android | 2016-06-03
Expected result:
ID = 1
ID = 2 is wrong, because it has only "Android"
ID = 3 is wrong, because it has "IOS"
I call this a set-within-sets query. I like to approach this using group by and having, because this is a very flexible method for expressing these conditions:
select id
from t
group by id
having sum(case when source = 'Android' then 1 else 0 end) > 0 and -- has Android
sum(case when source = 'Desktop' then 1 else 0 end) > 0 and -- has Desktop
sum(case when source not in ('Android', 'Desktop') then 1 else 0 end) = 0 -- has nothing else
Try this:
SELECT Id
FROM mytable
GROUP BY Id
HAVING COUNT(*) = COUNT(CASE WHEN Source='Android' THEN 1 END) +
COUNT(CASE WHEN Source='Desktop' THEN 1 END) AND
COUNT(CASE WHEN Source='Desktop' THEN 1 END) > 0 AND
COUNT(CASE WHEN Source='Android' THEN 1 END) > 0
Try this
select id from your_table
group by id
having
max(case when source in ('Android') then 1 else 0 end))=1
and
max(case when source in ('Desktop') then 1 else 0 end))=1
and count(distinct source)=2
Try to use
Select *
from yourtable a
where a.source = 'Android'
and not exist (Select b.id
from yourtable b
where b.id = a.id and b.source <> 'Android')

Insert Query in SQL Server

I have this table TAB
ID | Trans
------------
1 | 99
2 | 99
3 | 99
2 | 90
4 | 22
4 | 22
2 | 99
2 | 90
I want to get the count of trans that appear together for a single id, for eg, '1 has 90 & 99' and '2 has 90 & 99' in the entire table so the count is 2. This is similar to Apriori Algorithm's Third Step.
I tried this but didn't work-
select
count(trans)
from
Tab
where
trans = 99 & 90
group by
ID
having
ID = 2
My expected ans for Trans- 90 & 99 is
Trans1 | Trans2 | Support
------------
90 | 99 | 1
I also want to insert this into a new table.
If I understood your problem correctly, you can use SUM(CASE WHEN...END) for this:
DECLARE
#trans1 INT = 99,
#trans2 INT = 90
SELECT
ID,
PairCount =
CASE
WHEN SUM(CASE WHEN Trans = #trans1 THEN 1 ELSE 0 END) >= SUM(CASE WHEN Trans = #trans2 THEN 1 ELSE 0 END)
THEN SUM(CASE WHEN Trans = #trans2 THEN 1 ELSE 0 END)
ELSE SUM(CASE WHEN Trans = #trans1 THEN 1 ELSE 0 END)
END,
Trans1 = SUM(CASE WHEN Trans = #trans1 THEN 1 ELSE 0 END),
Trans2 = SUM(CASE WHEN Trans = #trans2 THEN 1 ELSE 0 END)
FROM DS
WHERE Trans IN (#trans1, #trans2)
GROUP BY ID
RESULT
ID PairCount Trans1 Trans2
----------- ----------- ----------- -----------
1 0 1 0
2 2 2 2
3 0 1 0

Count all rows from a column, and get 2 different count columns on the result even if there are not records to count

What I want to do is to count all rows from a column State, and get 2 different count columns on the result (one for each possible value 1 or 0).
The thing is that I need to show on the results 0 even if there are not records to count.
For example I have this table subcategory with this sample data:
SubcategoryID | Name |
---------------------------
201 | Name1 |
202 | Name2 |
203 | Name3 |
204 | Name4 |
And I have this table product with this sample data:
ProductID |Subcategory| State |
-------------------------------
101 | 201 | 1 |
102 | 201 | 1 |
103 | 201 | 1 |
104 | 202 | 0 |
105 | 202 | 0 |
106 | 203 | 1 |
107 | 203 | 0 |
108 | 203 | 0 |
State: 1 = Active, 0 = Inactive
So I want to get this:
|Subcategory| Active|Inactive|
------------------------------
| 201 | 3 | 0 |
| 202 | 0 | 2 |
| 203 | 1 | 2 |
| 204 | 0 | 0 |*
Please note that there are not products related to SubcategoryId = 204 on product table.
I'm using this query to get only the products that are related to one subcategory. Do you know what I have to add to get the line "|204|0|0|" ?
select p.subcategory,
sum(case when state = 1 then 1 else 0 end) as active,
sum(case when state = 0 then 1 else 0 end) as inactive
from product p
group by p.subcategory
Use Left Join to get all the rows from subcategory table and then do the Count.
SELECT S.subcategoryID,
Sum(CASE WHEN state = 1 THEN 1 ELSE 0 END) AS active,
Sum(CASE WHEN state = 0 THEN 1 ELSE 0 END) AS inactive
FROM subcategory s
LEFT JOIN product p
ON s.SubcategoryID = p.Subcategory
GROUP BY s.subcategoryID
Or use Count, case statement else part can be avoided
SELECT S.subcategoryID,
count(CASE WHEN state = 1 THEN 1 END) AS active,
count(CASE WHEN state = 0 THEN 1 END) AS inactive
FROM subcategory s
LEFT JOIN product p
ON s.SubcategoryID = p.Subcategory
GROUP BY s.subcategoryID
SQLFIDDLE DEMO
Just another way of doing it.
--Getting the counts for 'active' and 'inactive' states
SELECT S.subcategoryID,
count(CASE WHEN state = 1 THEN 1 END) AS active,
count(CASE WHEN state = 0 THEN 1 END) AS inactive
FROM subcategory s
JOIN product P ON s.SubcategoryID = p.Subcategory
GROUP BY s.subcategoryID
--Now adding subcategories which are only in subcategory table using `EXCEPT`.
UNION ALL
(
SELECT subcategoryid, 0, 0 from subcategory
EXCEPT
SELECT subcategory, 0, 0 from product
)

Count and Group By for products sold

I have a table Transaction & a table product as below .
Each Transaction has multiple products.
ProductId Name
1 ABC
2 DEF
3 GHI
Each transaction can have multiple products sold.
TransactionId ProductSoldInDept1 ProductSoldinDept2 ProductSoldinDept3
1 1 null null
2 1 2 null
3 3 1 null
4 2 3 1
I am planning to generate a report and I would like to get a result something like this :
This shows the number of products sold per each department grouped by Id
Expected Result :
ProductID Department1ProdCount Department2ProdCount Department3ProdCount
1 2 1 1
2 1 1 0
3 1 1 0
I could get till here , this is a query to get the counts for one specific product
which is productid : 1
I would like to know how I could use a group by here :
select Count(CASE WHEN ProductSoldInDept1 = 1 THEN 1 END) ,
Count(CASE WHEN ProductSoldInDept2 = 1 THEN 1 END) ,
Count(CASE WHEN ProductSoldInDept3 = 1 THEN 1 END)
from Table1
SELECT
p.ProductID,
Dept1ProdCount = COUNT(CASE WHEN t.ProductSoldInDept1 = p.ProductID THEN 1 END),
Dept2ProdCount = COUNT(CASE WHEN t.ProductSoldInDept2 = p.ProductID THEN 1 END),
Dept3ProdCount = COUNT(CASE WHEN t.ProductSoldInDept3 = p.ProductID THEN 1 END)
FROM dbo.Product AS p
LEFT OUTER JOIN dbo.[Transaction] AS t
ON p.ProductID IN
(t.ProductSoldInDept1, t.ProductSoldinDept2, t.ProductSoldinDept3)
GROUP BY p.ProductID;
Result
| PRODUCTID | DEPT1PRODCOUNT | DEPT2PRODCOUNT | DEPT3PRODCOUNT |
----------------------------------------------------------------
| 1 | 2 | 1 | 1 |
| 2 | 1 | 1 | 0 |
| 3 | 1 | 1 | 0 |
See a demo
Try this
select ProductID,
v.Department1ProdCount, v.Department2ProdCount, v.Department3ProdCount
from product a
cross apply
(
select Count(CASE WHEN ProductSoldInDept1 = a.ProductID THEN 1 END) Department1ProdCount,
Count(CASE WHEN ProductSoldInDept2 = a.ProductID THEN 1 END) Department2ProdCount,
Count(CASE WHEN ProductSoldInDept3 = a.ProductID THEN 1 END) Department3ProdCount
from Table1
) v
This:
TransactionId ProductSoldInDept1 ProductSoldinDept2 ProductSoldinDept3
1 1 null null
2 1 2 null
3 3 1 null
4 2 3 1
might be better structured as this:
transid prodsold deptid
1 1 1
2 1 1
2 2 2
3 3 1
3 1 2
4 2 1
4 3 2
4 1 3
I think that would make your queries easier to write.

Resources