SQL optimization GROUP BY same table, same column - sql

I have this GROUP BY query where I need to select some records from TABLE_1 and aggregate them.
The SELECTs are similar but I need to aggregate LON and BHAM separately as they are two different concepts but reside in same table.
My question is can I write the below differently in oracle that will optimize the performance of the query?
SELECT *
FROM (
( SELECT /*+ full(t1) */
t3.custId AS ID,
t2.secID AS SEC_ID,
t1.org_date AS SETT_DATE,
SUM(t1.amount) AS TOTAL
FROM test.TABLE_1 t1
INNER JOIN test.TABLE_2 t2 on t2.a_code = t1.a_code and t2.c_code = t1.c_code and t2.expiry_date > trunc(sysdate)
INNER JOIN test.TABLE_3 t3 on t3.account_id = t1.account_id
WHERE t1.city = 'LON'
AND t1.amount < 50000 and t1.amount > -50000
GROUP BY t3.custId, t2.secID, t1.org_date
)
UNION ALL
( SELECT /*+ full(t1) */
t3.custId AS ID,
t2.secID AS SEC_ID,
t1.org_date AS SETT_DATE,
SUM(t1.amount) AS TOTAL
FROM test.TABLE_1 t1
INNER JOIN test.TABLE_2 t2 on t2.a_code = t1.a_code and t2.c_code = t1.c_code and t2.expiry_date > trunc(sysdate)
INNER JOIN test.TABLE_3 t3 on t3.account_id = t1.account_id
WHERE t1.city = 'BHAM'
AND t3.alias = 'ABC'
AND t1.amount < 50000 and t1.amount > -50000
GROUP BY t3.custId, t2.secID, t1.org_date
)
)
ORDER BY ID, SEC_ID,
CASE WHEN SETT_DATE < TRUNC(sysdate) THEN trunc(sysdate) ELSE TRUNC(SETT_DATE) end

Remove union all and everything after, remove outer select, write where clause like here:
where -50000 < t1.amount and t1.amount < 50000
and (t1.city = 'LON' or (t1.city = 'BHAM' and t3.alias = 'ABC'))

You need to add the city column into the group by, and update the where clause to get both sets of rows, e.g.:
SELECT custid,
sec_id,
sett_date,
total
FROM (SELECT t3.custid AS id,
t2.secid AS sec_id,
CASE
WHEN t1.org_date < trunc(SYSDATE) THEN
trunc(SYSDATE)
ELSE
trunc(t1.org_date)
END AS sett_date,
t1.city,
SUM(t1.amount) AS total
FROM test.table_1 t1
INNER JOIN test.table_2 t2
ON t2.a_code = t1.a_code
AND t2.c_code = t1.c_code
AND t2.expiry_date > trunc(SYSDATE)
INNER JOIN test.table_3 t3
ON t3.account_id = t1.account_id
WHERE (t1.city = 'LON' OR (t1.city = 'BHAM' AND t3.alias = 'ABC'))
AND t1.amount < 50000
AND t1.amount > -50000
GROUP BY t3.custid,
t2.secid,
CASE
WHEN t1.org_date < trunc(SYSDATE) THEN
trunc(SYSDATE)
ELSE
trunc(t1.org_date)
END)
ORDER BY id,
sec_id,
sett_date;
However, I'm surprised you need both rows, since you can't tell which belongs to which city. I suspect you need to include the city (and maybe alias) columns into the final results.

Related

Join results of multiple select statements in sql

I have four select statements and I want to join them all to only get the common rows.
In an example, I'm providing 2 select statements:
SELECT
h.userid, 'Activity' as table_name,
h.stamp,
DATEDIFF(dd, kh.LatestDate, GETDATE()) as days_since,
m.group_name
FROM
([Animal].[SYSADM].[activity_history] h
INNER JOIN
(SELECT userid, MAX(stamp) as LatestDate
FROM [Animal].[SYSADM].[activity_history]
GROUP BY userid) kh ON h.userid = kh.userid AND h.stamp = kh.LatestDate)
LEFT OUTER JOIN
[Animal].[SYSADM].secure_member m ON m.user_name = h.userid
WHERE
(DATEDIFF(dd, kh.LatestDate, GETDATE()) > 90)
AND NOT (m.group_name = 'inactive')
ORDER BY
userid
SELECT
h.userid, 'Person' as table_name, h.stamp,
DATEDIFF(dd, kh.LatestDate, GETDATE()) as days_since,
m.group_name
FROM
([Animal].[SYSADM].[person_history] h
INNER JOIN
(SELECT userid, max(stamp) as LatestDate
FROM [Animal].[SYSADM].[person_history]
GROUP BY userid) kh ON h.userid = kh.userid AND h.stamp = kh.LatestDate)
LEFT OUTER JOIN
[Animal].[SYSADM].secure_member m ON m.user_name = h.userid
WHERE
(DATEDIFF(dd, kh.LatestDate, GETDATE()) > 90)
AND NOT (m.group_name = 'inactive')
ORDER BY
userid
I have tried INTERSECT, but it's not returning any rows, I want to see the common rows from both the select statements (actually I have 4 so I believe what works for 2 will work for 4)
Thanks in advance.
Update:
I tried inner join on 2 select statements and it gave me the desired result but now the question is how I can use inner join on 4 select statements.
SELECT DISTINCT t1.userid as A_UserID, t2.userid as P_UserID, t1.stamp as A_stamp, t2.stamp as P_stamp, datediff(dd,t1.stamp,GetDate()) as A_days_since, datediff(dd,t2.stamp,GetDate()) as P_days_since, t1.group_name, t1.table_name, t2.table_name
from
(SELECT h.userid, 'Activity' as table_name, h.stamp, datediff(dd,kh.LatestDate,GetDate()) as days_since, m.group_name
FROM
( [Animal].[SYSADM].[activity_history] h
inner join (
select userid, max(stamp) as LatestDate
from [Animal].[SYSADM].[activity_history]
group by userid
) kh on h.userid = kh.userid and h.stamp = kh.LatestDate
)
left outer join [Animal].[SYSADM].secure_member m on m.user_name = h.userid
where
(datediff(dd,kh.LatestDate, GetDate()) > 90)
and not (m.group_name = 'inactive')) t1
inner join
(SELECT h.userid, 'Person' as table_name, h.stamp, datediff(dd,kh.LatestDate,GetDate()) as days_since, m.group_name
FROM
( [Animal].[SYSADM].[person_history] h
inner join (
select userid, max(stamp) as LatestDate
from [Animal].[SYSADM].[person_history]
group by userid
) kh on h.userid = kh.userid and h.stamp = kh.LatestDate
)
left outer join [Animal].[SYSADM].secure_member m on m.user_name = h.userid
where
(datediff(dd,kh.LatestDate, GetDate()) > 90)
and not (m.group_name = 'inactive')) t2
on
t1.userid = t2.userid
order by T1.userid
Query Result
Forget about the UNION for a moment. Imagine you take that result and insert into Table1
Then depend what you mean the "common rows". If you want exact value but in different tables
SELECT userid, h.stamp, days_since, m.group_name
FROM Table1
GROUP BY userid, h.stamp, days_since, m.group_name
HAVING COUNT( table_name ) = 2 -- in this case 2 because are two types
-- Activity and Persons
After viewing your query result you also need to add DISTINCT to each of the queries on the UNION.

Counting records on multiple conditions

My query is to count the number of transactions that are happened in the tables. Below is my query:
select tt.transaction_key, tt.description, t.mode, count(t.TransactionType) as Frequency
from transaction_names tt left join
transaction_report_data t
on tt.transaction_key = t.TransactionType and
t.created >='2017-04-11' and t.created <= '2018-04-13'
group by tt.transaction_key, tt.description, t.mode;
I also need the count/frequency for the number of transactions which are successful only or which has t.status = 'success'.
Please help how can I adjust this 'where' clause to count this type of records also.
Thanks in advance.
You can do a case with aggregate like below
select
tt.transaction_key,
tt.description,
t.mode,
count(t.TransactionType) as Frequency,
Sum(case when t.status='success' then 1 else 0 end) as SuccessfulTransactions
from transaction_names tt left join
transaction_report_data t
on tt.transaction_key = t.TransactionType and
t.created >='2017-04-11' and t.created <= '2018-04-13'
group by tt.transaction_key, tt.description, t.mode;
Try this
SELECT
tt.transaction_key,
tt.[Description],
t.mode,
COUNT(t.TransactionType) AS Frequency,
Success = SUM(CASE WHEN T.[Status]='Success' THEN 1 ELSE 0 END)
FROM transaction_names tt
LEFT JOIN transaction_report_data t
ON tt.transaction_key = t.TransactionType
AND t.created >= '2017-04-11'
AND t.created <= '2018-04-13'
GROUP BY
tt.transaction_key,
tt.[Description],
t.mode;
select tt.transaction_key, tt.description, t.mode, count(t.TransactionType) as Frequency
from transaction_names tt left join
transaction_report_data t
on tt.transaction_key = t.TransactionType and
t.created >='2017-04-11' and t.created <= '2018-04-13'
group by tt.transaction_key, tt.description, t.mode, t.status
having t.status = 'success'

Getting error tb_sales_person_source.id is invalid in the select list in SQL Server

I am new to SQL Server, I have below query which is working fine in Mysql server, but it is not working in SQL Server, I get an error:
tb_sales_person_source.id is invalid in the select list
Can anyone please help me how can I resolve this error ?
SELECT
tb_sales_person_source.id,
tb_sales_person_source.name,
tb_sales_person_source.display_name,
tb_sales_person_source.mapped_sales_person_source_id,
tb_sales_person_source.company_id,
tb_sales_person_source.gm_created,
tb_sales_person_source.gm_modified,
COUNT(tb_Episode.id) AS total_soc,
SOCDate, MonthSOC
FROM
tb_Episode
JOIN
tb_sales_person_source ON tb_sales_person_source.id = tb_Episode.sales_referral_source_id
WHERE
(BranchID = '238' OR BranchID = '239' OR BranchID = '240' OR BranchID = '241')
AND tb_Episode.CustID = '27'
AND PayerType = 'Ep'
AND SOC = 1
AND SOCDate >= '2016-04-01'
AND SOCDate < '2017-5-01'
GROUP BY
sales_referral_source_id, MonthSOC
ORDER BY
tb_sales_person_source.id ASC, tb_Episode.SOCDate ASC;
In SQL server, you must have to add all the columns of "SELECT" in "Group by" apart from the column which is used in the aggregate function
SELECT tb_sales_person_source.id
,tb_sales_person_source.NAME
,tb_sales_person_source.display_name
,tb_sales_person_source.mapped_sales_person_source_id
,tb_sales_person_source.company_id
,tb_sales_person_source.gm_created
,tb_sales_person_source.gm_modified
,count(tb_Episode.id) AS total_soc
,SOCDate
,MonthSOC
FROM tb_Episode
JOIN tb_sales_person_source ON tb_sales_person_source.id = tb_Episode.sales_referral_source_id
WHERE (
BranchID = '238'
OR BranchID = '239'
OR BranchID = '240'
OR BranchID = '241'
)
AND tb_Episode.CustID = '27'
AND PayerType = 'Ep'
AND SOC = 1
AND SOCDate >= '2016-04-01'
AND SOCDate < '2017-5-01'
GROUP BY sales_referral_source_id
,MonthSOC
,tb_sales_person_source.id
,tb_sales_person_source.NAME
,tb_sales_person_source.display_name
,tb_sales_person_source.mapped_sales_person_source_id
,tb_sales_person_source.company_id
,tb_sales_person_source.gm_created
,tb_sales_person_source.gm_modified
,SOCDate
ORDER BY tb_sales_person_source.id ASC
,tb_Episode.SOCDate ASC
It is impossible to accurately re-write your query until you update it to make it clear which table each column comes from.
The important piece of information you need to be aware of, however, is that when using GROUP BY every field in the SELECT must either have an aggregate function around it (MIN(), or MAX(), or SUM(), etc, etc), or be mentioned in the GROUP BY.
This means that the following is NOT valid SQL...
SELECT
t1.some_id,
t1.name,
t1.whatever,
COUNT(t2.id),
SUM(t2.value)
FROM
t1
INNER JOIN
t2
ON t2.some_id = t1.some_id
GROUP BY
t1.some_id
Instead you need one of the following...
SELECT
t1.some_id ,
t1.name,
t1.whatever,
COUNT(t2.id),
SUM(t2.value)
FROM
t1
INNER JOIN
t2
ON t2.some_id = t1.some_id
GROUP BY
t1.some_id,
t1.name,
t1.whatever
Or...
SELECT
t1.some_id,
MAX(t1.name),
MAX(t1.whatever),
COUNT(t2.id),
SUM(t2.value)
FROM
t1
INNER JOIN
t2
ON t2.some_id = t1.some_id
GROUP BY
t1.some_id
The same is true even if you are grouping by a column from table 2.
So, this is invalid too...
SELECT
t1.some_id,
t1.name,
t2.a_date,
COUNT(t2.id),
SUM(t2.value)
FROM
t1
INNER JOIN
t2
ON t2.some_id = t1.some_id
GROUP BY
t2.a_date
This time, however, you have an extra option. Use a sub-query to group up the data in table 2 first...
SELECT
t1.some_id,
t1.name,
t2_agg.a_date,
t2_agg.count_rows,
t2_agg.total_value
FROM
t1
INNER JOIN
(
SELECT
some_id,
a_date,
COUNT(t2.id) AS count_rows,
SUM(t2.value) AS total_value
FROM
t2
GROUP BY
some_id,
a_date
)
t2_agg
ON t2_agg.some_id = t1.some_id
If you fully qualify your query (so I can see which table every column comes from) then I can show you how to use this method to suit your exact case.
Finally i resolved my query, here is my query, thanks to all of you for your help
SELECT tb_sales_person_source.id, tb_sales_person_source.name, tb_sales_person_source.display_name, count(tb_Episode.id) as total_soc, MonthSOC
FROM tb_Episode
JOIN tb_sales_person_source ON tb_sales_person_source.id = tb_Episode.sales_referral_source_id
WHERE ( BranchID = '238' or BranchID = '239' or BranchID = '240' or BranchID = '241')
AND tb_Episode.CustID = '27'
AND PayerType = 'Ep'
AND SOC = 1
AND SOCDate >= '2016-04-01'
AND SOCDate < '2017-5-01'
GROUP BY tb_sales_person_source.id, tb_sales_person_source.name, tb_sales_person_source.display_name, sales_referral_source_id, MonthSOC
ORDER BY tb_sales_person_source.id asc

Write query using JOINS

select
store, sum(value)
from
rms.sa_tran_head
where
store_day_seq_no in (select store_day_seq_no
from rms.sa_store_day
where store in (3003)
and business_date = '01-JAN-2015')
and tran_type in ('SALE', 'RETURN')
group by
store;
How to write the above query using JOINS..
SELECT
sd.store,
SUM(TH.VALUE) AS GROSS ,
SUM(ti.qty) AS QTY
FROM rms.sa_tran_head AS th
JOIN rms.sa_store_day AS sd
ON th.store_day_seq_no = sd.store_day_seq_no
JOIN rms.sa_tran_item AS ti
ON ti.tran_seq_no = th.tran_seq_no
WHERE sd.store in (3003) --in (3003) use in if more than 1 value
AND sd.business_date = '01-JAN-2015'
AND th.tran_type IN ('SALE','RETURN')
GROUP BY
sd.store;
When I add other columns of another table it is showing different values...
I assumed store_day_seq_no is FK for the table rms.sa_store_day and the query with JOIN like this,
SELECT
sd.store,
SUM(sd.value) AS [Sum]
FROM rms.sa_tran_head AS th
JOIN rms.sa_store_day AS sd
ON th.store_day_seq_no = sd.store_day_seq_no
WHERE
sd.store = 3003 --in (3003) use in if more than 1 value
AND sd.business_date = '01-JAN-2015'
AND th.tran_type IN ('SALE','RETURN')
GROUP BY
sd.store;
I think this is ok.
SELECT
t1.store,
SUM(t1.Value) AS Sum_Value
FROM rms.sa_tran_head t1
INNER JOIN sa_store_day t2 ON t1.store_day_seq_no = t2.store_day_seq_no
WHERE t2.store IN ( 3003 )
AND t2.business_date = '01-JAN-2015'
AND t1.tran_type IN ( 'SALE' , 'RETURN' )
GROUP BY t1.store

inner join results of "with" clause

I have 2 with clauses like this:
WITH T
AS (SELECT tfsp.SubmissionID,
tfsp.Amount,
tfsp.campaignID,
cc.Name
FROM tbl_FormSubmissions_PaymentsMade tfspm
INNER JOIN tbl_FormSubmissions_Payment tfsp
ON tfspm.SubmissionID = tfsp.SubmissionID
INNER JOIN tbl_CurrentCampaigns cc
ON tfsp.CampaignID = cc.ID
WHERE tfspm.isApproved = 'True'
AND tfspm.PaymentOn >= '2013-05-01 12:00:00.000' AND tfspm.PaymentOn <= '2013-05-07 12:00:00.000')
SELECT SUM(Amount) AS TotalAmount,
campaignID,
Name
FROM T
GROUP BY campaignID,
Name;
and also:
WITH T1
AS (SELECT tfsp.SubmissionID,
tfsp.Amount,
tfsp.campaignID,
cc.Name
FROM tbl_FormSubmissions_PaymentsMade tfspm
INNER JOIN tbl_FormSubmissions_Payment tfsp
ON tfspm.SubmissionID = tfsp.SubmissionID
INNER JOIN tbl_CurrentCampaigns cc
ON tfsp.CampaignID = cc.ID
WHERE tfspm.isApproved = 'True'
AND tfspm.PaymentOn >= '2013-05-08 12:00:00.000' AND tfspm.PaymentOn <= '2013-05-14 12:00:00.000')
SELECT SUM(Amount) AS TotalAmount,
campaignID,
Name
FROM T1
GROUP BY campaignID,
Name;
Now I want to join the results of the both of the outputs. How can I do it?
Edited: Added the <= cluase also.
Reults from my first T:
Amount-----ID----Name
1000----- 2-----Annual Fund
83--------1-----Athletics Fund
300-------3-------Library Fund
Results from my T2
850-----2-------Annual Fund
370-----4-------Other
The output i require:
1800-----2------Annual Fund
83-------1------Athletics Fund
300------3-------Library Fund
370------4-----Other
You don't need a join. You can use
SELECT SUM(tfspm.PaymentOn) AS Amount,
tfsp.campaignID,
cc.Name
FROM tbl_FormSubmissions_PaymentsMade tfspm
INNER JOIN tbl_FormSubmissions_Payment tfsp
ON tfspm.SubmissionID = tfsp.SubmissionID
INNER JOIN tbl_CurrentCampaigns cc
ON tfsp.CampaignID = cc.ID
WHERE tfspm.isApproved = 'True'
AND ( tfspm.PaymentOn BETWEEN '2013-05-01 12:00:00.000'
AND '2013-05-07 12:00:00.000'
OR tfspm.PaymentOn BETWEEN '2013-05-08 12:00:00.000'
AND '2013-05-14 12:00:00.000' )
GROUP BY tfsp.campaignID,
cc.Name
If I am right, after a WITH-clause you have to immediatly select the results of that afterwards. So IMHO your best try to achieve joining the both would be to save each of them into a temporary table and then join the contents of those two together.
UPDATE: after re-reading your question I realized that you probably don't want a (SQL-) join but just your 2 results packed together in one, so you could easily achieve that with what I descibed above, just select the contents of both temporary tables and put a UNION inbetween them.
I was thinking it wrongly. Thanks for the help. This is how i achieved what exactly i want:
WITH
T AS (
SELECT tfsp.SubmissionID , Amount1 =
CASE
WHEN tfspm.PaymentOn >= '2013-01-10 11:34:54.000' AND tfspm.PaymentOn <= '2013-04-10 11:34:54.000' THEN tfsp.Amount
END
, Amount2 =
CASE
WHEN tfspm.PaymentOn >= '2013-05-01 11:34:54.000' AND tfspm.PaymentOn <= '2013-05-23 11:34:54.000' THEN tfsp.Amount
END
, tfsp.campaignID , cc.Name FROM tbl_FormSubmissions_PaymentsMade tfspm
INNER JOIN tbl_FormSubmissions_Payment tfsp ON tfspm.SubmissionID = tfsp.SubmissionID
INNER JOIN tbl_CurrentCampaigns cc ON tfsp.CampaignID = cc.ID
WHERE tfspm.isApproved = 'True'
)
SELECT ISNULL(SUM(Amount1),0) AS TotalAmount1, ISNULL(SUM(Amount2),0) AS TotalAmount2, campaignID , Name FROM T GROUP BY campaignID, Name;