I have two tables CDmachine and trnasaction.
CDMachine Table with columns CDMachineID, CDMachineName, InstallationDate
Transaction table with columns TransactionID,CDMachineID,TransactionTime,Amount
I am calculating revenue using the below query but it eliminates the machine without any transaction
SELECT CDMachine.MachineName,
SUM(Transaction.Amount)
FROM CDMachine
LEFT JOIN TRANSACTION ON CDMachine.CDMachineID = Transaction.CDMachineID
WHERE Transaction.TransactionTime BETWEEN '2019-01-01' AND '2019-01-31'
GROUP BY CDMachine.CDMachineName
ORDER BY 2
Move the WHERE condition to the ON clause:
select m.MachineName, sum(t.Amount)
from CDMachine m left join
Transaction t
on m.CDMachineID = t.CDMachineID and
t.TransactionTime between '2019-01-01' and '2019-01-31'
group by m.CDMachineName
order by 2;
The WHERE clause turns the outer join to an inner join -- meaning that you are losing the values that do not match.
If you want 0 rather than NULL for the sum, then use:
select m.MachineName, coalesce(sum(t.Amount), 0)
Even though you are using a LEFT JOIN, the fact that you have a filter on a column from the joined table causes rows that don't meet the join condition to be removed from the result set.
You need to apply the filter on transaction time to the transactions table, before joining it or as part of the join condition. I would do it like this:
SELECT CDMachine.MachineName,
SUM(Transaction.Amount)
FROM CDMachine
LEFT JOIN (
SELECT * FROM TRANSACTION
WHERE Transaction.TransactionTime BETWEEN '2019-01-01' AND '2019-01-31'
) AS Transaction
ON CDMachine.CDMachineID = Transaction.CDMachineID
GROUP BY CDMachine.CDMachineName
ORDER BY 2
Related
Below query is to return flag as Y if c.LAST_UPDATED_TIMESTAMP < MAX(t.LATEST_ACTION_TIMESTAMP)
SELECT
'Y' as CAN_UPDATE, t.LATEST_ACTION_TIMESTAMP
FROM CUSTOMERS c
LEFT JOIN TRANSACTIONS t ah on (c.customer_id = t.customer_id)
WHERE
t.status_active_flag = 'Y' and c.customer_ID ='CUST_019'
GROUP BY t.LATEST_ACTION_TIMESTAMP
HAVING c.LAST_UPDATED_TIMESTAMP < MAX(t.LATEST_ACTION_TIMESTAMP);
ORA-00979 not a GROUP BY expression encountered, understand that all columns in SELECT need to be included in GROUP BY clause. How can handle for the flagged value 'Y' in this case?
Column c.LAST_UPDATED_TIMESTAMP need to be added to group by part as well
GROUP BY t.LATEST_ACTION_TIMESTAMP, c.LAST_UPDATED_TIMESTAMP
here is a dbfiddle with a dumb example
The HAVING clause will effectively change the LEFT JOIN to an INNER JOIN and you can, since you are taking the maximum of the column you are grouping by and aggregating on that column is irrelevant as there will only be a singular value per group, then move the HAVING comparison to the ON clause of the join without aggregation:
SELECT 'Y' as CAN_UPDATE,
t.LATEST_ACTION_TIMESTAMP
FROM CUSTOMERS c
INNER JOIN TRANSACTIONS t
ON ( c.customer_id = t.customer_id
AND c.LAST_UPDATED_TIMESTAMP < t.LATEST_ACTION_TIMESTAMP
)
WHERE t.status_active_flag = 'Y'
AND c.customer_id ='CUST_019'
GROUP BY t.LATEST_ACTION_TIMESTAMP;
You could also use a DISTINCT (or UNIQUE) clause instead of the GROUP BY clause.
I am making up a SQL query which will get all the transaction types from one table, and from the other table it will count the frequency of that transaction type.
My query is this:
with CTE as
(
select a.trxType,a.created,b.transaction_key,b.description,a.mode
FROM transaction_data AS a with (nolock)
RIGHT JOIN transaction_types b with (nolock) ON b.transaction_key = a.trxType
)
SELECT COUNT (trxType) AS Frequency, description as trxType,mode
from CTE where created >='2017-04-11' and created <= '2018-04-13'
group by trxType ,description,mode
The transaction_types table contains all the types of transactions only and transaction_data contains the transactions which have occurred.
The problem I am facing is that even though it's the RIGHT join, it does not select all the records from the transaction_types table.
I need to select all the transactions from the transaction_types table and show the number of counts for each transaction, even if it's 0.
Please help.
LEFT JOIN is so much easier to follow.
I think you want:
select tt.transaction_key, tt.description, t.mode, count(t.trxType)
from transaction_types tt left join
transaction_data t
on tt.transaction_key = t.trxType and
t.created >= '2017-04-11' and t.created <= '2018-04-13'
group by tt.transaction_key, tt.description, t.mode;
Notes:
Use reasonable table aliases! a and b mean nothing. t and tt are abbreviations of the table name, so they are easier to follow.
t.mode will be NULL for non-matching rows.
The condition on dates needs to be in the ON clause. Otherwise, the outer join is turned into an inner join.
LEFT JOIN is easier to follow (at least for people whose native language reads left-to-right) because it means "keep all the rows in the table you have already read".
I have a code that pulls all of a Reps new customers and their year to date sales. The only problem is it only pulls customers that have sales in the invdate range, but I need it to show all of the accounts with a 0 if they do not have any sales. Is there any way to achieve this? I tried using COALESCE and it didn't seem to work. I also tried using left, right, full outer joins. Any help would be appreciated!
select
a.Acctnum,
sum(a.invtotal) as total
from invoices a right join accounts b on a.acctnum = b.acctnum where
a.invdate between '1/1/2017' and '12/31/2017'
and a.sls = '78'
and b.sls = '78'
and b.activetype = 'y' and b.startdate > (getdate()-365)
group by a.acctnum
order by total desc
You are restricting your results in your WHERE clause AFTER you join your table causing records to drop. Instead, switch to a LEFT OUTER JOIN with your accounts table driving the query. Then restrict your invoices table in your ON clause so that invoices are dropped BEFORE you join.
SELECT a.Acctnum,
sum(a.invtotal) AS total
FROM accounts b
LEFT OUTER JOIN invoices a ON
a.accntnum = b.acctnum AND
--Put the restrictions on your left most table here
--so they are removed BEFORE joining.
a.invdate BETWEEN '1/1/2017' AND '12/31/2017'
AND a.sls = '78'
WHERE
b.sls = '78'
AND b.activetype = 'y'
AND b.startdate > (getdate() - 365)
GROUP BY a.acctnum
ORDER BY total DESC
It's a bit like doing a subquery in invoices before joining in as the left table. It's just easier to drop the conditions into the ON clause.
Your problem is you where clauses are changing the right join to an inner join. Put all the ones that are aliased by a. into the ON clause.
I have two tables, transactions and dates. One date may have one or more transactions. I need to get a list of dates with or without transactions of a specific account (account number 111 below).
select d.the_date, t.account, t.amount from dates as d
LEFT OUTER JOIN transactions as t ON t.tx_date=d.the_date
where t.account=111 AND d.the_date>='2016-01-02' and d.the_date<='2017-12-30'
order by d.the_date;
The issue is that when I specify in the condition t.account=111 I don't get the dates on which account 111 did NOT make any transactions.
Only if I remove from the condition t.account=111 I DO get the dates with no transactions (i.e. the LEFT OUTER JOIN works). Why does this happen?
Conditions on the second table need to go into the on clause:
select d.the_date, t.account, t.amount
from dates d left join
transactions t
on t.tx_date = d.the_date and t.account = 111
where d.the_date >= '2016-01-02' and d.the_date <= '2017-12-30'
order by d.the_date;
Otherwise, the value of t.account is NULL in the where clause, turning the outer join into an inner join.
I have four tables,in which First has one to many relation with rest of three tables named as (Second,Third,Fourth) respectively.I want to sum only Distinct Rows returned by select query.Here is my query, which i try so far.
select count(distinct First.Order_id) as [No.Of Orders],sum( First.Amount) as [Amount] from First
inner join Second on First.Order_id=Second.Order_id
inner join Third on Third.Order_id=Second.Order_id
inner join Fourth on Fourth.Order_id=Third.Order_id
The outcome of this query is :
No.Of Orders Amount
7 69
But this Amount should be 49,because the sum of First column Amount is 49,but due to inner join and one to many relationship,it calculate sum of also duplicate rows.How to avoid this.Kindly guide me
I think the problem is cartesian products in the joins (for a given id). You can solve this using row_number():
select count(t1234.Order_id) as [No.Of Orders], sum(t1234.Amount) as [Amount]
from (select First.*,
row_number() over (partition by First.Order_id order by First.Order_id) as seqnum
from First inner join
Second
on First.Order_id=Second.Order_id inner join
Third
on Third.Order_id=Second.Order_id inner join
Fourth
on Fourth.Order_id=Third.Order_id
) t1234
where seqnum = 1;
By the way, you could also express this using conditions in the where clause, because you appear to be using the joins only for filtering:
select count(First.Order_id) as [No.Of Orders], sum(First.Amount) as [Amount]
from First
where exists (select 1 from second where First.Order_id=Second.Order_id) and
exists (select 1 from third where First.Order_id=third.Order_id) and
exists (select 1 from fourth where First.Order_id=fourth.Order_id);