Select entire row with more than one distinct column - sql

I have a table based on invoice items where I am trying to use SQL to detect at what dates the price or currency for the combination of material/customer has changed. The table contains invoices for several customers although the materials can be common.
My SQL skills are quite basic and I have tried several different approaches using GROUP BY and DISTINCT that I have found in other threads but I always seem to get stuck somewhere along the way.
This is basically what the data looks like:
Invoice Inv. Date Material Price Currency Per/Qty Customer
SE100 20140901 111111 1 EUR 1 840006
SE100 20140901 222222 2 EUR 1000 840006
SE100 20140901 333333 3 USD 1 840006
SE101 20140902 111111 1 EUR 1 840006
SE101 20140902 222222 2 EUR 1000 840006
SE101 20140902 333333 3 USD 1 840006
SE102 20140903 111111 2 EUR 1 840006
SE102 20140903 222222 2 USD 1000 840006
SE102 20140903 333333 3 USD 1 840006
SE103 20140904 111111 1 EUR 1 840006
SE103 20140904 222222 2 USD 1000 840006
SE103 20140904 333333 3 USD 1 840006
What I want to accomplish is basically to select the first row datewise for all distinct combinations of Customer/Material/Currency/Price and then subselect the entire rows (sorted by material) for those materials that occur more than once in the selection, thus indicating the price or currency has changed from the initial value.
The expected output from the query using the data in the table above would then look something like this:
Invoice Inv. Date Material Price Currency Per/Qty Customer
SE100 20140901 111111 1 EUR 1 840006
SE102 20140903 111111 2 EUR 1 840006
SE103 20140904 111111 1 EUR 1 840006
SE100 20140901 222222 2 EUR 1000 840006
SE102 20140903 222222 2 USD 1000 840006
I hope I managed to explain the problem in an understandable way. The database engine is SQL Server 2005 Express.
Any help would be appreciated...

The key word DISTINCT in SQL has the meaning of "unique value". When applied to a column in a query it will return as many rows from the result set as there are unique, different values for that column. As a consequence it creates a grouped result set, and values of other columns are random unless defined by other functions (such as max, min, average, etc.)
If you meant to say you want to return all rows for which Col 06 has a specific value, then use the "where Col 06 = value" clause
SELECT mt.*
FROM (
SELECT DISTINCT col6
FROM mytable
) mto
JOIN mytable mt
ON mt.id =
(
SELECT TOP 1 id
FROM mytable mti
WHERE mti.col6 = mto.col6
-- ORDER BY
-- id
-- Uncomment the lines above if the order matters
)

I think this is a direct translation of what you want:
select t.*
from mydata t join
(select Customer, Material, count(distinct price) as numprices
from mydata
group by Customer, Material
having count(distinct price) > 1
) cmcp
on t.customer = cmcp.customer and t.material = cmcp.material;
This leaves out the currency. Unfortunately, SQL Server doesn't support multiple arguments to distinct. You can include it this way:
select t.*
from mydata t join
(select Customer, Material,
count(distinct cast(price as varchar(255)) + ':' + currency) as numprices
from mydata
group by Customer, Material
having count(distinct cast(price as varchar(255)) + ':' + currency) > 1
) cmcp
on t.customer = cmcp.customer and t.material = cmcp.material;
Most databases support window/analytic functions, so you can also phrase this as:
select t.*
from (select t.*,
min(cast(price as varchar(255)) + ':' + currency)) over (partition by Customer, Material) as minprice,
max(cast(price as varchar(255)) + ':' + currency)) over (partition by Customer, Material) as maxprice
from mydata t
) t
where minprice <> maxprice
order by Material, Inv_Date;

Related

T-SQL count number of different values across multiple columns

I have an SQL database which contains a table with four different currency columns, I need to determine how many different currencies each record has across these four columns, for example..
Record ID
Curr1
Curr2
Curr3
Curr4
CurrencyCount
1
GBP
USD
GBP
GBP
2
2
GBP
EUR
GBP
USD
3
3
GBP
GBP
GBP
GBP
1
4
GBP
GBP
GBP
EUR
2
How can I determine the "CurrencyCount" / count of currencies against each record? I can't think how to approach this
Thank you for any help
You can use a Table Value Constructor referencing columns from the outer row and then COUNT (DISTINCT - no need for any expanding out and collapsing with GROUP BY
SELECT *,
CurrrencyCount = (SELECT COUNT (DISTINCT Currency) FROM (VALUES (CURR1), (CURR2), (CURR3), (CURR4)) AS x (Currency))
FROM YourTable
This would do the work. But not sure how will be the performance if there is a large amount of data.
WITH CTE as (
SELECT '1' as RecID,'GBP' as CURR1,'USD' as CURR2,'GBP' as CURR3, 'GBP' as CURR4
UNION ALL
SELECT '2' ,'GBP' ,'EUR' ,'GBP','USD'
UNION ALL
SELECT '3' ,'GBP' ,'GBP' ,'GBP','GBP'
UNION ALL
SELECT '4' ,'GBP' ,'GBP' ,'GBP','EUR'
)
SELECT
RecID
,CURR1,CURR2,CURR3,CURR4
,COUNT(DISTINCT Currency) as CurrrencyCount
FROM CTE
CROSS APPLY (VALUES (CURR1), (CURR2), (CURR3), (CURR4)) as x (Currency)
GROUP BY RecID,CURR1,CURR2,CURR3,CURR4
Only run this its also work
SELECT
RecordID
,Curr1,Curr2,Curr3,Curr4
,COUNT(DISTINCT Currency) as Currency
FROM [TABLE_NAME]
CROSS APPLY (VALUES (Curr1), (Curr2), (Curr3),(Curr4)) as x (Currency)
GROUP BY RecordID,Curr1,Curr2,Curr3,Curr4

SQL Insert two rows in other table based on If confition

I have 2 table which is DATA and MAIN. DATA table is the raw data extract from excel and MAIN table is the data after validation(few rules) have been made.
Rule:
If Amoun1<>'' and Amount2<>''.
Insert 2 row in MAIN table. The first row will have the value for Amount and Percentage from Amount2 & Percentage2 with TaxRateType = Taxable.
And the second row will have Amount and Percentage get from Amount1 & Percentage1 with TaxRateType = EXEMPT. The invoiceNo also will be add with '_1'
If Amount2<>'' and Amount1=''
Insert 1 row with Amount and Percentage from Amount2 & Percentage2 with TaxRateType = Taxable.
Else
Insert 1 row with Amount and Percentage get from Amount1 & Percentage1 with TaxRateType = EXEMPT
The example is as below table:
**DATA**
InvoiceNo | TotalAmount | Percentage1 | Amount1 | Percentage2 | Amount2
abc123 100 5 45 20 55
abc124 60 5 60 20
abc125 50 5 22 50
**MAIN**
InvoiceNo | Percentage | Amount | TaxRateType | ReferenceValue
abc123 20 55 TAXABLE 2
abc123_1 5 45 EXEMPT 1
abc124 5 60 EXEMPT 1
abc125 22 50 TAXABLE 2
I'm stuck in here for 4 hours searching for which method to use. Currently I have an idea to use if exists but still it's not correct and somehow I feel its not a good method.
IF EXISTS (SELECT ID from [alcatel].[Main_Temp] where Amount0<>'' and Amount21<>'')
BEGIN
INSERT INTO [alcatel].[Main]
( [Country],[InvoiceNo],[Amount],[Percentage],[TaxRateType],[Reference Value])
SELECT
[Country],[InvoiceNo],[Amount2],[Percentage2],'TAXABLE' as [TaxRateType],2 as [Reference Value]
FROM [alcatel].[Data];
INSERT INTO [alcatel].[Main]
( [Country],[InvoiceNo],[Amount],[Percentage],[TaxRateType],[Reference Value])
SELECT
[Country],[InvoiceNo]+'_1' as InvoiceNo,[Amount1],[Percentage1],'EXEMPT' as [TaxRateType],'1' as [Reference Value]
FROM [alcatel].[Data];
END
Followed with other condition.
I think you just want to unpivot the data with some logic:
select invoiceno + v.suffix, v.percentage, v.amount,
v.taxratetype, v.referencevalue
from data d cross apply
(values (1, d.Percentage1, d.Amount1, 'EXEMPT', ''),
(2, d.Percentage2, d.Amount2, 'TAXABLE', (case when d.amount1 is not null then '_1' else '' end))
) v(ReferenceValue, Percentage, Amount, TaxRateType, Suffix)
where amount is not null;
Here is a db<>fiddle.

sql Query to find different record in a column

I have table with 5 OR 6 COLUMNS and I need to use the below 2 columns to get the result
col1 col2
Acc1 USD
ACC1 GBP
ACC1 EUR
ACC2 USD
Result:
I need to find out if a acc has more than 2 currency, but the base currency is USD. I need to find out those records which has USD plus other currency if I have only USD accounts then it should not come in my result.
With the information provided this could be an answer:
SELECT col1
FROM tab
GROUP BY col1
HAVING count(*) > 1
Not the best solution but should do the job.
with cte as
(
SELECT t1.[col1],t1.[col2],(select count(t2.col1) from accounts t2 where t2.col1=t1.col1) as AllCurrency
from accounts t1
)
SELECT distinct cte.col1 from cte where cte.AllCurrency>1

Selecting Account Numbers with latest date

I have been trying to solve this problem now for days.
I have table called Stat with the following simplified structure and sample data:
Customer BankID AccNumb Type Date Amount AccType
Customer 1 Boa 5 Account Statement 2015-01-01 10000,00 Eur
Customer 1 CS 10 Account Statement 2015-04-04 22000,00 Eur
Customer 2 Sa 15 Account Statement 2015-03-13 3000,00 Eur
Customer 2 Sa 40 Account Statement 2015-04-24 1000,00 Eur
Customer 2 Sa 15 Sale Advice 2015-04-16 400,00 Eur
Customer 2 Sa 15 Account Statement 2015-12-24 50,00 Usd
Customer 2 Boa 20 Sale Advice 2015-05-15 6000,00 Eur
Customer 3 Cu 25 Account Statement 2015-11-27 81000,00 Eur
Customer 3 Cu 30 Sale Advice 2015-11-27 3000,00 Usd
Customer 3 Pop 30 Account Statement 2015-11-27 12000,00 Eur
What I'm trying to do is to Select the AccountNumber with the latest date specified. A Customer can also have different Account Numbers on various Banks, so it should also be grouped by BankID and Customer.
I have come this far:
SELECT AccNumb, Customer, BankID,
(SELECT TOP 1 Amount FROM Stat
WHERE AccNumb = y.AccNumb AND Customer = y.Customer AND
BankID = y.BankID AND Type = 'Account Statement' AND
Date = MAX(y.Date) GROUP BY Amount) Amount
FROM Stat y
GROUP BY AccNumb, Customer, BankID
ORDER BY Customer, AccNumb
And it works fine, the problem is i should also add the column AccType and Date
I managed to do this with 2 more subselects (the query takes long but it works).
But now i have the problem that there are also NULL values in Customer (or Date) Column. Now, the account number of these 'NULL' Customers still should be displayed if it's the latest date. I also tried to do the same by joining the table by itself, and it didnt work out.
SELECT x.AccNumber, x.Customer, x.BankID, x.Date, y.Amount, y.AccType
FROM Stat y RIGHT JOIN
(SELECT AccNumber, Customer, BankID, MAX(Date) Date FROM Stat
GROUP BY AccNumber, Customer, BankID) x
ON x.AccNumber = y.AccNumber AND
x.Customer = y.Customer AND
x.BankID = y.BankID AND
x.Date = y.Date
ORDER BY y.Customer, y.AccNumber
But now the 'NULL' Customers only have NULL values in the Amount, Date and AccType Columns, which is not correct.
The output should be something like this
AccNumb Customer BankID Amount Date AccType
111111111 a Boa 1234.40 31.06.2014 Eur
222222222 NULL Boa 5678.40 31.04.2014 Eur
333333333 b Boa 0.00 25.02.2014 Eur
444444444 NULL Boa 9101.40 23.04.2015 Eur
555555555 NULL Boa 1213.40 31.02.2014 Usd
A66666666 c Sa NULL 31.02.2014 Eur
777777777 c Sa 1415.00 31.12.2014 Eur
888888888 c Boa 1617.40 31.12.2014 Usd
999999999 f Pop 5678.64 31.10.2014 Eur
Thanks in advance.
Just use row_number(), if I understand correctly:
select s.*
from (select s.*,
row_number() over (partition by customer, bankId order by date desc) as seqnum
from stat s
) s
where seqnum = 1;
Find the rows with latest dates, i.e. return a row if no other row with same AccountNumber, BankID and Customer but a later Date exists:
select *
from stat s1
where not exists (select 1 from stat s2
where s1.AccountNumber = s2.AccountNumber
and s1.BankID = s2.BankID
and s1.Customer = s2.Customer
and s1.Date < s2.Date)
You're first query works closely to what, I believe, you are looking for. Using it as a base, we can alter to work for you:
SELECT
AccNumb,
Customer,
BankID,
Amount,
Date,
AccType
FROM Stat y
WHERE Date = (SELECT MAX(z.DATE)
FROM Stat z
WHERE z.AccNumb = y.AccNumb
AND z.Customer = y.Customer
AND z.BankID = y.BankID AND Type = 'Account Statement')
ORDER BY Customer, AccNumb

How to calculate

I have a table like this:
Item Qty Price A Price B
abc 5 36.00 0
qwe 8 0 48.00
zxc 6 12.00 0
poi 4 10.00 0
lkj 9 12.00 0
mnb 3 0 14.00
vfr 7 0 6.00
How can I sum the value using SQL ie. if Price A is zero, it will pick Price B. The expected results will be as follows :-
Item Value
abc 180.00
qwe 384.00
zxc 72.00
poi 40.00
lkj 36.00
mnb 42.00
vfr 42.00
SELECT
ITEM
, (Qty * [Price A]) + (Qty * [Price B]) AS Value
FROM
TableName
Use the following if A and B could be non-zero, and you only want to use price A when this occurs:
SELECT Item, CASE WHEN `Price A` != 0 THEN `Price A` * Qty
ELSE `Price B` * Qty
END AS Value
FROM table;
Why not do something like
SELECT SUM(Price A) FROM table; SELECT SUM(Price B) FROM table WHERE Price A = 0;
And then add the results together...
from the example above you could really just select priceA + priceB, since one or the other is always zero, otherwise... I am not quite sure this can be done in pure SQL...
Assuming both can be not null:
SELECT item, QTY * IF(PriceA!=0, PriceA, PriceB) AS VALUE from tableName;