How to combine two selects with different where clauses? - sql

select kota ,total, totalsum from
(
SELECT i.[Antam_Unit] as kota ,count(p.[Id_Pks_Pk])as total FROM [SIMPKBL].[dbo].[Pks_Pk] p join [SIMPKBL].[dbo].[Par_Unit_Antam] i on i.Id_Unit_Antam = p.Id_Unit_Antam group by i.[Id_Unit_Antam],i.Antam_Unit
UNION ALL
SELECT i.[Antam_Unit] as kota , count(p.[Id_Proposal_Pk])as totalsum FROM [SIMPKBL].[dbo].[Pks_Pk] p join [SIMPKBL].[dbo].[Par_Unit_Antam] i on i.Id_Unit_Antam = p.Id_Unit_Antam where YEAR(p.Tanggal_Cetak_Pks_Pk) = '2012' group by i.[Id_Unit_Antam],i.Antam_Unit
) t
group by kota,total
I want an output like this:
kota total totalsum
A 12 4
B 16 5

Since the queries are so similar it appears that they can be combined into one select:
select i.[Antam_Unit] as kota,
count(p.[Id_Pks_Pk]) as total,
count( case when YEAR(p.Tanggal_Cetak_Pks_Pk) = '2012' then p.[Id_Proposal_Pk] else null end ) as totalsum
FROM [SIMPKBL].[dbo].[Pks_Pk] p join [SIMPKBL].[dbo].[Par_Unit_Antam] i on i.Id_Unit_Antam = p.Id_Unit_Antam
group by i.[Id_Unit_Antam], i.Antam_Unit

If I understood your requirements correctly, the result you are looking for can be achieved using this simple statement:
SELECT i.[Antam_Unit] as kota,
count(p.[Id_Pks_Pk]) as total,
SUM(CASE WHEN YEAR(p.Tanggal_Cetak_Pks_Pk) = '2012' AND p.[Id_Proposal_Pk] IS NOT NULL THEN 1 ELSE 0 END) as totalsum
FROM [SIMPKBL].[dbo].[Pks_Pk] p
join [SIMPKBL].[dbo].[Par_Unit_Antam] i
on i.Id_Unit_Antam = p.Id_Unit_Antam
group by i.[Id_Unit_Antam],i.Antam_Unit

Related

SQL Server : how to group only part of the syntax

I have a problem creating a SQL Server query.
In summary, the query should get columns that are sum and count, grouped by customerID, and another column that is a case when by a column that is not used as a grouper column.
My problem is to group only part of the syntax, while the case when column does not need to be grouped.
A sample data, Test:
customerID, 1,2,3,4...
InvoiceID, 1234551, 1234552...
ProductID, A, B, C...
Date, Datetime
Income, int
customerID
InvoiceID
ProductID
Date
Income
1
1234551
A
01/01/2015
300
2
1234552
B
02/01/2016
300
I have a solution, but I am sure there is a more simple solution.
SELECT DISTINCT
Test.CustomerId,
ISNULL(TBL.Income_2015, 0) AS Income_2015,
ISNULL(TBL_2.Income_2016, 0) AS Income_2016,
CASE
WHEN Test.ProductID = 'A'
THEN 'TRUE'
ELSE 'FALSE'
END AS 'purchase_product_A',
TBL_3.Invoices
FROM
Test
LEFT OUTER JOIN
(SELECT CustomerId, SUM(Income) AS Income_2015
FROM Test
WHERE YEAR(Date) = 2015
GROUP BY CustomerId) TBL ON Test.customerID = TBL.customerID
LEFT OUTER JOIN
(SELECT CustomerId, SUM(Income) AS Income_2016
FROM Test
WHERE YEAR(Date) = 2016
GROUP BY CustomerId) TBL_2 ON Test.customerID = TBL_2.customerID
LEFT OUTER JOIN
(SELECT CustomerId, COUNT(InvoiceID) AS Invoices
FROM Test
GROUP BY CustomerId) TBL_3 ON Test.customerID = TBL_3.customerID
To produce:
customerID, 1,2,3...
Income_2015, int
Income_2016, int
Invoices, int
Purchase_product_A, boolean
customerID
Income_2015
Income_2016
Invoices
Purchase_product_A
1
300
300
2
TRUE
10
0
400
1
FALSE
Thanks!
Nir
You may use conditional aggregation with a single pass query:
SELECT
CustomerId,
SUM(CASE WHEN YEAR(Date) = 2015 THEN Income ELSE 0 END) AS Income_2015,
SUM(CASE WHEN YEAR(Date) = 2016 THEN Income ELSE 0 END) AS Income_2016,
COUNT(InvoiceID) AS Invoices,
CASE WHEN COUNT(CASE WHEN ProductID = 'A' THEN 1 END) > 0
THEN 'TRUE' ELSE 'FALSE' END AS [Purchase_product_A]
FROM Test
GROUP BY
CustomerId;

aliasing pivot column

i am trying to alias the pivot column to scenario1, scenario2, scenario3 instead of 1,2,3. I am getting error.
select *
from (select *
from (select s.campaign_id campaign_id, s.scenario_index scenario_index
from scenario s, campaign c where s.campaign_id = c.campaign_id)
pivot (max(scenario_index)for scenario_index in (1,2,3))
)a
thank you, aggregation gives the result with alias now. The requirement i have is to combine these columns with another query which is
select CASE WHEN AWARD_TYPE = 0 THEN award_rate||' points'
when AWARD_TYPE = 1 then Award_rate||' %'
when award_type=2 then RATIO_POINTS||' points per '||RATIO_MON_UNIT||' AED' End
from points_rule p
where c.pt_basic_rule_id = p.point_rule_id ) as pool_awards,
this query comes as a column and then the scenario1, 2,3 should come as 3 columns with the value of the pool_award based on the campaign_id
Just use conditional aggregation:
select s.campaign_id,
max(case when scenario_index = 1 then 1 end) as scenario1,
max(case when scenario_index = 2 then 1 end) as scenario2,
max(case when scenario_index = 3 then 1 end) as scenario3
from scenario s join
campaign c
on s.campaign_id = c.campaign_id
group by campaign_id;
You can use an alias in IN clause of the PIVOT as follows:
select *
from (select *
from (select s.campaign_id campaign_id, s.scenario_index scenario_index
from scenario s, campaign c where s.campaign_id = c.campaign_id)
pivot (max(scenario_index)for scenario_index in (1 as scenario1,2 as scenario2,3 as scenario3))
)a

Display SQL Data From Row to Column

My SQL statement is list as below:
SELECT Count(DISTINCT A.LM_PERSON_ID) AS HEAD_COUNT
,A.LM_STATUS
,To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') AS YEAR
,B.LM_COURSE_NAME AS COURSE_NAME
FROM LM_ENRLOLMENT A
,LM_COURSE_TBL B
WHERE A.LM_STATUS='COMP'
AND A.LM_COURSE_ID=B.LM_CI_ID
GROUP BY A.LM_STATUS_CHANGE_DT,LM_STATUS,B.LM_COURSE_NAME
The example output I would like to display was:
COURSE_NAME 2010 2011 2012
A 4 5 1
B 2 1 1
C 6 0 3
D 1 1 2
But the main problem I am facing is that LM_STATUS_CHANGE_DT is dynamic data. Is there anyone that can show me how I could do this?
You did not specify what RDBMS you are using but you should be able to use the following in all versions:
SELECT
B.LM_COURSE_NAME AS COURSE_NAME,
count(DISTINCT case when To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') = '2010' then A.LM_PERSON_ID end) as Year2010,
count(DISTINCT case when To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') = '2011' then A.LM_PERSON_ID end) as Year2011,
count(DISTINCT case when To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') = '2012' then A.LM_PERSON_ID end) as Year2012
FROM LM_ENRLOLMENT A
INNER JOIN LM_COURSE_TBL B
ON A.LM_COURSE_ID=B.LM_CI_ID
WHERE A.LM_STATUS='COMP'
GROUP BY B.LM_COURSE_NAME
If you are using an RDBMS that has the PIVOT function (SQL Server 2005+/Oracle 11g+) then your code will be similar to this:
SELECT *
FROM
(
SELECT DISTINCT B.LM_COURSE_NAME,
To_Char(A.LM_STATUS_CHANGE_DT,'YYYY') As Year,
A.LM_PERSON_ID
FROM LM_ENRLOLMENT A
INNER JOIN LM_COURSE_TBL B
ON A.LM_COURSE_ID=B.LM_CI_ID
WHERE A.LM_STATUS='COMP'
) src
PIVOT
(
count(LM_PERSON_ID)
for Year in ('2010', '2011', '2012')
) piv

Wrong Result in SUM()

SQL Server 2000 - 4 tables, 3 columns each. Personal ID (COTA), User (Telegestionador), and an amount-of-work value.
Tables are: Contactados, NC, FQ, OT
Example of one of them.
XAV045 QUIPILDORY 26
XAV045 QUIPILDORY 29
XAV045 QUIPILDORY 21
XAV045 QUIPILDORY 39
XAV052 LOPEZRA 29
XAV052 LOPEZRA 39
XAV052 LOPEZRA 24
XAV052 LOPEZRA 36
What I need is. A result-view with ID, USER and then the fields with the sum of the amount of work grouped by personal id/user. (there are 4 tables, so in the final view I should have 6 columns)
So, first row should be
XAV045,QUIPILDORY, 115, X, Y, Z
Being X, Y, Z the results of SUM() from the other 3 tables.
First try is this:
SELECT
dbo.Contactados.COTA, dbo.Contactados.telegestionador,
SUM(dbo.Contactados.Total) AS Total,
SUM(dbo.OT.Total) AS [Cont-Der],
SUM(dbo.FQ.FQ) AS Cerrados,
SUM(dbo.NC.Total) AS NC
FROM
dbo.Contactados
LEFT OUTER JOIN
dbo.OT ON dbo.Contactados.COTA = dbo.OT.COTA AND dbo.Contactados.telegestionador = dbo.OT.telegestionador AND dbo.Contactados.FGfin = dbo.OT.FGfin
LEFT OUTER JOIN
dbo.FQ ON dbo.Contactados.COTA = dbo.FQ.COTA AND dbo.Contactados.telegestionador = dbo.FQ.telegestionador AND dbo.Contactados.FGfin = dbo.FQ.FGfin
LEFT OUTER JOIN
dbo.NC ON dbo.Contactados.COTA = dbo.NC.COTA AND dbo.Contactados.telegestionador = dbo.NC.telegestionador AND dbo.Contactados.FGfin = dbo.NC.FGfin
GROUP BY
dbo.Contactados.telegestionador, dbo.Contactados.COTA
It throws wrong results, I know GROUP BY groups the results, not the table rows individually. But I can't find the proper way to do so.
Any help?
Assuming that all four tables have identical datatypes for the four columns, you can use a union all to list all values, and then sum those as part of a subquery:
SELECT
COTA
,Telegestionador
,SUM(CASE WHEN table_name = 'Contactados' THEN work_value ELSE 0 END) AS Contactados_sum
,SUM(CASE WHEN table_name = 'NC' THEN work_value ELSE 0 END) AS nc_sum
,SUM(CASE WHEN table_name = 'FQ' THEN work_value ELSE 0 END) AS fq_sum
,SUM(CASE WHEN table_name = 'QT' THEN work_value ELSE 0 END) AS qt_sum
FROM
(
SELECT
COTA
,Telegestionador
,work_value
,'Contactados' as table_name
FROM Contactados
UNION ALL
SELECT
COTA
,Telegestionador
,work_value
,'NC' as table_name
FROM NC
UNION ALL
SELECT
COTA
,Telegestionador
,work_value
,'FQ' as table_name
FROM FQ
UNION ALL
SELECT
COTA
,Telegestionador
,work_value
,'QT' as table_name
FROM QT
) summary
GROUP BY
COTA
,Telegestionador

Subselect Query Improvement

How can I improve the SQL query below (SQL Server 2008)? I want to try to avoid sub-selects, and I'm using a couple of them to produce results like this
StateId TotalCount SFRCount OtherCount
---------------------------------------------------------
AZ 102 50 52
CA 2931 2750 181
etc...
SELECT
StateId,
COUNT(*) AS TotalCount,
(SELECT COUNT(*) AS Expr1 FROM Property AS P2
WHERE (PropertyTypeId = 1) AND (StateId = P.StateId)) AS SFRCount,
(SELECT COUNT(*) AS Expr1 FROM Property AS P3
WHERE (PropertyTypeId <> 1) AND (StateId = P.StateId)) AS OtherCount
FROM Property AS P
GROUP BY StateId
HAVING (COUNT(*) > 99)
ORDER BY StateId
This may work the same, hard to test without data
SELECT
StateId,
COUNT(*) AS TotalCount,
SUM(CASE WHEN PropertyTypeId = 1 THEN 1 ELSE 0 END) as SFRCount,
SUM(CASE WHEN PropertyTypeId <> 1 THEN 1 ELSE 0 END) as OtherCount
FROM Property AS P
GROUP BY StateId
HAVING (COUNT(*) > 99)
ORDER BY StateId
Your alternative is a single self-join of Property using your WHERE conditions as a join parameter. The OtherCount can be derived by subtracting the TotalCount - SFRCount in a derived query.
Another alternative would be to use the PIVOT function like this:
SELECT StateID, [1] + [2] AS TotalCount, [1] AS SFRCount, [2] AS OtherCount
FROM Property
PIVOT ( COUNT(PropertyTypeID)
FOR PropertyTypeID IN ([1],[2])
) AS pvt
WHERE [1] + [2] > 99
You would need to add an entry for each property type which could be daunting but it is another alternative. Scott has a great answer.
If PropertyTypeId is not null then you could do this with a single join. Count is faster than Sum. But is Count plus Join faster than Sum. The test case below mimics your data. docSVsys has 800,000 rows and there are about 300 unique values for caseID. The Count plus Join in this test case is slightly faster than the Sum. But if I remove the with (nolock) then Sum is about 1/4 faster. You would need to test with your data.
select GETDATE()
go;
select caseID, COUNT(*) as Ttl,
SUM(CASE WHEN mimeType = 'message/rfc822' THEN 1 ELSE 0 END) as SFRCount,
SUM(CASE WHEN mimeType <> 'message/rfc822' THEN 1 ELSE 0 END) as OtherCount,
COUNT(*) - SUM(CASE WHEN mimeType = 'message/rfc822' THEN 1 ELSE 0 END) as OtherCount2
from docSVsys with (nolock)
group by caseID
having COUNT(*) > 1000
select GETDATE()
go;
select docSVsys.caseID, COUNT(*) as Ttl
, COUNT(primaryCount.sID) as priCount
, COUNT(*) - COUNT(primaryCount.sID) as otherCount
from docSVsys with (nolock)
left outer join docSVsys as primaryCount with (nolock)
on primaryCount.sID = docSVsys.sID
and primaryCount.mimeType = 'message/rfc822'
group by docSVsys.caseID
having COUNT(*) > 1000
select GETDATE()
go;