Different results in SQL based on what columns I display - sql

I am trying to run a query to gather the total items on hand in our database. However it seems i'm getting incorrect data. I am selecting selecting just the amount field and summing it using joins from separate tables based on certain parameters, however if I display additional fields such as order number, and date all of a sudden im getting different data, even though those fields are being used as filters in the query. Is it because its not in the select statement? If it needs to be in the select statement is it possible to not display them?
Here are the two queries.
-- Items On Hand
select CONVERT(decimal(25, 2), SUM(tw.amount)) as 'Amt'
from [Sales Header] sh
join
(
select *
from TWAllOrders
where [Status] like 'Released'
) tw
on tw.[Order Nb] = sh.No_
join
(
select *
from OnHand
) oh
on tw.No_ = oh.[Item No_]
where sh.[Requested Delivery Date] < getdate()
HAVING SUM(tw.Quantity) <= SUM(oh.Qty)
providing a sum of 21667457.20
and with the added columns
-- Items On Hand
select CONVERT(decimal(25, 2), SUM(tw.amount)) as 'Amt', [Requested Delivery Date], sh.No_, tw.[Status]
from [Sales Header] sh
join
(
select *
from TWAllOrders
where [Status] like 'Released'
) tw
on tw.[Order Nb] = sh.No_
join
(
select *
from OnHand
) oh
on tw.No_ = oh.[Item No_]
where sh.[Requested Delivery Date] < getdate()
group by sh.[Requested Delivery Date], sh.No_, tw.[Status]
HAVING SUM(tw.Quantity) <= SUM(oh.Qty)
order by sh.[Requested Delivery Date] ASC
Providing a sum of 12319998
I'm self taught in SQL so I may be misunderstanding something obvious, thanks for the help.

With no sample data, I am going to have to demonstrate this in principle. In the latter query you have a GROUP BY meaning the scope of the values in the HAVING will differ, and thus the filtering from said HAVING will be different.
Let's take the following sample data:
CREATE TABLE dbo.MyTable (Grp char(1),
Quantity int,
Required int);
INSERT INTO dbo.MyTable (Grp, Quantity, [Required])
VALUES('a',2,7),
('a',14,2),
('b',4, 7),
('b',3,4),
('c',17,5);
Now we'll perform an overly simplified version of your query:
SELECT SUM(Quantity)
FROM dbo.MyTable
HAVING SUM(Quantity) > SUM(Required);
This brings back the value 40; which is the SUM of all the values in Quantity. A value is returned because the total SUM of Required is 25.
Now let's add a GROUP BY like your second query:
SELECT SUM(Quantity)
FROM dbo.MyTable
GROUP BY Grp
HAVING SUM(Quantity) > SUM(Required);
Now we have 2 rows, with the values 16 and 17 giving a total value of 33. That's because the rows where Grp have a value of 'B' are filtered out, as the SUM of Quantity is lower that Required for 'B'.
The same is happening in your data; in the grouped data you have groups where the HAVING condition isn't met, so those rows aren't returned.

Related

How to get the sum of transaction amount happened in one date?

I am trying to write a query that will give me transaction amount sum happened in one date. The problem is , when I added column date in my query, I get individual values not their sum. The requirement for this query is to have one entry for each merchant but i am getting multiple rows for one merchant.
SELECT SUBSTR(m.MERCHANTLASTNAME, 1, 36) Name1,
m.MERCHANTBANKBSB MerchantAccbsb,
m.MERCHANTBANKACCNR Merchant_act,
m.MERCHANTID merchantid,
t.transactiondate date1,
sum(t.TRANSACTIONAMOUNT) as total
FROM fss_merchant m
JOIN fss_terminal term
ON m.MERCHANTID = term.MERCHANTID
JOIN FSS_DAILY_TRANSACTION t
ON term.TERMINALID = t.TERMINALID
group by t.transactiondate, SUBSTR(m.MERCHANTLASTNAME, 1, 36), m.MERCHANTID, m.MERCHANTBANKBSB, m.MERCHANTBANKACCNR,
m.MERCHANTLASTNAME
Output of my query:
I want to get one entry per each merchant with the sum of transaction amount in one day, not multiple rows of transaction in that day.
You can calculate the total amount in different inner query with the truncated date and join it with FSS_MERCHANT table so that issues described by #SatishSK and #mangusta is taken care.
You can use the following query:
SELECT
SUBSTR(M.MERCHANTLASTNAME, 1, 36) NAME1,
M.MERCHANTBANKBSB MERCHANTACCBSB,
M.MERCHANTBANKACCNR MERCHANT_ACT,
M.MERCHANTID MERCHANTID,
M_DATA.TRANSACTIONDATE DATE1,
M_DATA.TOTAL AS TOTAL
FROM
FSS_MERCHANT M
INNER JOIN (
SELECT
TERM.MERCHANTID MERCHANTID,
TRUNC(T.TRANSACTIONDATE) TRANSACTIONDATE,
SUM(T.TRANSACTIONAMOUNT) AS TOTAL
FROM
FSS_TERMINAL TERM
JOIN FSS_DAILY_TRANSACTION T ON TERM.TERMINALID = T.TERMINALID
GROUP BY
TERM.MERCHANTID,
TRUNC(T.TRANSACTIONDATE)
) M_DATA ON ( M.MERCHANTID = M_DATA.MERCHANTID );
Good luck!!
t.transactiondate column might contain date+time values. Use TRUNC(t.transactiondate) where you are using just t.transactiondate. You will get sum(transaction amount) "Date-wise" for each merchant.
OR
Filter out rows based on "Date" value in WHERE clause to retrieve data pertaining to a specific date.
Probably the reason is that you have included both m.MERCHANTLASTNAME and SUBSTR(m.MERCHANTLASTNAME,1,36) into the group by clause.
In case if there are entries with same SUBSTR(m.MERCHANTLASTNAME,1,36) but different m.MERCHANTLASTNAME, this is going to yield duplicates. You need to remove m.MERCHANTLASTNAME from group by clause

Conditional left join on max date and where clause in second table

I am attempting to join a customer table with sales table where I show the list of all customers in database and any paid sale the customer might have in the sales tables. Now a customer can have multiple sales rows in the sales table.
This is an example sales record of one customer with multiple sales in the sale tables
while extracting this record I would like to get only the MAX (q_saledatetime) WHERE the q_paidamount is > 0.
as in show me the last time this customer made a payment to us. So in this case row 2 where they paid 8.90 is what I would like to get for that customer. If a customer has no record in the sales table, show their name/details on the list either way.
My failure at the moment is how to include the where clause of the paid amount + max date column.
ATTEMPT A
select DISTINCT ON (q_customer.q_code)
q_customer.q_code, q_customer.q_name, -- customer info
MAX(q_saleheader.q_saledatetime) AS latestDate, q_saleheader.q_paidamount -- saleheader info
FROM q_customer
LEFT JOIN q_saleheader ON (q_customer.q_code = q_saleheader.q_customercode)
group by q_customer.q_code, q_customer.q_name , q_saleheader.q_saledatetime, q_saleheader.q_paidamount
order by q_customer.q_code ASC
which results in
so for Fred Blogg is picking up details from row 4 instead of 2 (first image). As there's no rule for q_paidamount at this point
ATTEMPT B
SELECT
customer.q_code, customer.q_name, -- customer info
sale.q_saledatetime, sale.q_paidamount -- sale info
FROM q_customer customer
LEFT JOIN (SELECT * FROM q_saleheader WHERE q_saledatetime =
(SELECT MAX(q_saledatetime) FROM q_saleheader b1 where q_paidamount > 0 ))
sale ON sale.q_customercode = customer.q_code
which results in
This doesnt seem to be getting any information from the sale table at all.
Update:
After having a closer look at my first attempt I amended the statement and came up with this solution which achieves the same results as Michal's answer. I just curious to know is there any pitfalls or perfomance disadvantages with the following way.
select DISTINCT ON (q_customer.q_code)
q_customer.q_code, q_customer.q_name, -- customer info
q_saleheader.q_saledatetime, q_saleheader.q_paidamount -- saleheader info
FROM q_customer
LEFT JOIN q_saleheader ON (q_customer.q_code = q_saleheader.q_customercode AND
q_saleheader.q_paidamount > 0 )
group by q_customer.q_code, q_customer.q_name , q_saleheader.q_saledatetime,
q_saleheader.q_paidamount
order by q_customer.q_code ASC, q_saleheader.q_saledatetime DESC
main change was adding AND q_saleheader.q_paidamount > 0 on the join and q_saleheader.q_saledatetime DESC to make sure are getting the top row of that related data. As mentioned both Michal's answer and this solution achieve the same results. Just curious about pitfalls in either of the two ways.
Try this query:
SELECT c.q_code,
c.q_name,
CASE WHEN q_saledatetime <> '1900-01-01 00:00:00.000' THEN q_saledatetime END q_saledatetime,
q_paidamount
FROM (
SELECT c.q_code,
c.q_name,
coalesce(s.q_saledatetime, '1900-01-01 00:00:00.000') q_saledatetime, --it will indicate customer with no data
s.q_paidamount,
ROW_NUMBER() OVER (PARTITION BY c.q_code ORDER BY COALESCE(s.q_saledatetime, '1900-01-01') DESC) rn
FROM q_customer c
LEFT JOIN (SELECT q_saledatetime,
q_paidamount
FROM q_saleheader
WHERE q_paidamount > 0) s
ON c.q_code = s.q_customercode
) c WHERE rn = 1

Get max date (last date sale) column against each item no?

I have following SQL Query which return result
of itemno and no of total quantity sale.I want itemdescripton column against each item no as well.ITEMDESC# column in table invitems.
SQL QUERY :
select INITEMS.ITEMNO,(COUNT(INITEMS.ITEMNO)*COUNT(INITEMS.QTY)) 'Item
Sale',INITEMS.ITEMDESC#1 from InvItems INITEMS
INNER JOIN InvHdr HDR ON INITEMS.INVNO=HDR.INVNO
WHERE INITEMS.TYPE='3'
GROUP BY INITEMS.ITEMNO,INITEMS.ITEMDESC#1
I want max date (last date item sale) column in result.Date column in InvHdr table against each InvNo
As explain in the earlier comments, your query does not works because you have different ITEMDESC for the same ITEMNO
This you "gives" you the result that you want
select INITEMS.ITEMNO,
MAX(INITEMS.ITEMDESC#1) AS 'ITEMDESC',
(COUNT(INITEMS.ITEMNO)*COUNT(INITEMS.QTY)) 'Item Sale'
FROM InvItems INITEMS
INNER JOIN InvHdr HDR ON INITEMS.INVNO = HDR.INVNO
WHERE INITEMS.TYPE = '3'
GROUP BY INITEMS.ITEMNO
But you must check why there are such as in your data. You can list out those ITEMNO that is of such case
SELECT ITEMNO
FROM InvItems
GROUP BY ITEMNO
HAVING MAX(ITEMDESC#1) <> MIN(ITEMDESC#1)
Assuming you are working with an SQL Server version higher than 2005 (which is a pretty safe assumption these days), you can use the over clause with aggregating functions, thus potentially eliminating the need for the group by clause (that is potentially since group by will return distinct results for each value (or set of values) that exists in the group by clause - so you might also need to use distinct:
SELECT initems.itemno,
COUNT(initems.itemno) OVER (PARTITION BY initems.itemno)
* SUM(initems.qty) OVER (PARTITION BY initems.itemno)
FROM InvItems AS initems
WHERE initems.type = '3'

SQL Server adjust each value in a column by another table

I have two tables, TblVal and TblAdj.
In TblVal I have a bunch of values that I need adjusted according to TblAdj for a given TblVal.PersonID and TblVal.Date and then returned in some ViewAdjustedValues. I must apply only those adjustments where TblAdj.Date >= TblVal.Date.
The trouble is that since all the adjustments are either a subtraction or a division, they need to be made in order. Here is the table structure:
TblVal: PersonID, Date, Value
TblAdj: PersonID, Date, SubtractAmount, DivideAmount
I want to return ViewAdjustedValues: PersonID, Date, AdjValue
Can I do this without iterating through TblAdj using a WHILE loop and an IF block to either subtract or divide as necessary? Is there some nested SELECT table magic I can perform that would be faster?
I think you can do it without a loop, but whether you want to or not is another question. A query that I think works is below (SQL Fiddle here). The key ideas are as follows:
Each SubtractAmount has the ultimate effect of subtracting SubtractAmount divided by the product of all later DivideAmounts for the same PersonID. The Date associated with the PersonID isn't relevant to this adjustment (fortunately). The CTE AdjustedAdjustments contains these adjusted SubtractAmount values.
The initial Value for a PersonID gets divided by the product of all DivideAmount values on or after that persons Date.
EXP(SUM(LOG(x))) works as an aggregate product if all values of x are positive. You should constrain your DivideAmount values to assure this, or adjust the code accordingly.
If there are no DivideAmounts, the associated product is NULL and changed to 1. Similarly, NULL sums of adjusted SubtractAmount values are changed to zero. A left join is used to preserve an values that are not subject to any adjustments.
SQL Server 2012 supports an OVER clause for aggregates, which was helpful here to aggregate "all later DivideAmounts."
WITH AdjustedAdjustments AS (
select
PersonID,
Date,
SubtractAmount/
EXP(
SUM(LOG(COALESCE(DivideAmount,1)))
OVER (
PARTITION BY PersonID
ORDER BY Date
ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING
)
) AS AdjustedSubtract,
DivideAmount
FROM TblAdj
)
SELECT
p.PersonID,
p.Value/COALESCE(EXP(SUM(LOG(COALESCE(DivideAmount,1)))),1)
-COALESCE(SUM(a.AdjustedSubtract),0) AS AmountAdjusted
FROM TblVal AS p
LEFT OUTER JOIN AdjustedAdjustments AS a
ON a.PersonID = p.PersonID
AND a.Date >= p.Date
GROUP BY p.PersonID, p.Value, p.Date;
Try something like following:
with CTE_TblVal (PersonID,Date,Value)
as
(
select A.PersonID, A.Date, A.Value
from TblVal A
inner join TblAdj B
on A.PersonID = B.PersonID
where B.Date >= A.Date
)
update CTE_TblVal
set Date = TblAdj.Date,
Value = TblAdj.Value
from CTE_TblVal
inner join TblAdj
on CTE_Tblval.PersonID = TblAdj.PersonID
output inserted.* into ViewAdjustedValues
select * from ViewAdjustedValues

SQL - Restricting JOINed Records by Date

Using two tables in MSSQL:
One table, [CUSTOMER], containing information on donors.
- Relevent columns: [CustID], [Name]
One table, [DONATION], containing records for each donation given.
- Relevent columns: [CustID], [Amount], [Date]
The tables share a key, [CustID].
I want to aggregate the [Amounts] according to each [CustID]
SELECT DONATION.CUSTID
,PEOPLE.NAME
,SUM (DONATION.AMOUNT) as TOTAL_DONATION
FROM [dbo].[DONATION] INNER JOIN [dbo].[PEOPLE] ON DONATION.CUSTID = PEOPLE.CUSTID
GROUP BY
DONATION.CUSTID
,PEOPLE.NAME
HAVING SUM (DONATION.AMOUNT) > 100
This query works fine, even with adding the HAVING clause.
When I want to restrict the dates of donations to aggregate (adding to the SELECT,GROUP BY, and HAVING clauses) however...
SELECT DONATION.CUSTID
,PEOPLE.NAME,DONATION.DATE
,SUM (DONATION.AMOUNT) as TOTAL_DONATION
FROM [dbo].[DONATION] INNER JOIN [dbo].[PEOPLE] ON DONATION.CUSTID = PEOPLE.CUSTID
GROUP BY
DONATION.CUSTID
,PEOPLE.NAME
,DONATION.DATE
HAVING SUM (DONATION.AMOUNT) > 100
and DONATION.DATE > '1-1-2010'
The query no longer returns aggregate sums of each person's donations, but returns individual donations for each person, which meet the HAVING criteria.
How can I implement this date restriction? Is it how I'm joining or summing or....? Thanks.
Move it to the WHERE clause
SELECT DONATION.CUSTID
,PEOPLE.NAME
,SUM (DONATION.AMOUNT) as TOTAL_DONATION
FROM [dbo].[DONATION]
INNER JOIN [dbo].[PEOPLE] ON DONATION.CUSTID = PEOPLE.CUSTID
WHERE DONATION.DATE > '1-1-2010'
GROUP BY
DONATION.CUSTID
,PEOPLE.NAME
HAVING SUM (DONATION.AMOUNT) > 100
What this means:
- given the people who donated
- look only at data where donation date is in 2010 or later
- and within that data, show the people who donated a total of more than 100
Modify your first query like:
,SUM (CASE WHEN DONATION.DATE > '1-1-2010' THEN DONATION.AMOUNT END)
as TOTAL_DONATION
You can't group by date unless you want one row per customer per date.
How about doing it from the donation table and just looking up customer names?
select Donator=(select name from [people] where [people].custid=[donation].custid),
custid,SUM(amount)
from [donation]
where [donation].date between '1/1/2011' and '1/15/2011'
group by custid
having SUM(amount)>100