I am trying to write a code that is dynamic for European countries to capture the appropriate price based on the certain country. I am trying to pull data where the List Price is less than 4000, but wanted to add a statement that if the Currency is in SEK, then I want to multiply that list price by 10 and then if it's less than 4000 I want to include it. For other Currencies just take the list price as is for currency codes EUR, GBP, and CHF.
For example this is what I have and it's not working correctly:
SELECT
[Country_IBS]
,[Item_Number]
,[Price_List]
,List_Price
,[Currency]
FROM #temp4
WHERE List_Price < 4000
OR (SELECT List_Price * 10 WHERE Currency = 'SEK') < 4000
AND Currency = 'EUR'
OR Currency = 'GBP'
OR Currency = 'CHF'
I would do:
where
list_price * (case when currency = 'SEK' then 10 else 1 end) < 4000
and currency in ('EUR', 'GBP', 'CHF', 'SEK')
Or you can use the more verbose form:
where
(currency in ('EUR', 'GBP', 'CHF') and list_price < 4000)
or (currency = 'SEK' and price < 400)
AND has higher precedence than OR. You need to club you conditions in parentheses.
Your query should be like this:
--version 1: Using OR
SELECT
[Country_IBS]
,[Item_Number]
,[Price_List]
,List_Price
,[Currency]
FROM #temp4
WHERE (List_Price < 4000 AND Currency IN ('EUR','GBP','CHF'))
OR (List_Price * 10 < 4000 AND Currency = 'SEK')
--Version 2: Using UNION ALL
SELECT
[Country_IBS]
,[Item_Number]
,[Price_List]
,List_Price
,[Currency]
FROM #temp4
WHERE List_Price < 4000 AND Currency IN ('EUR','GBP','CHF')
UNION ALL
SELECT
[Country_IBS]
,[Item_Number]
,[Price_List]
,List_Price
,[Currency]
FROM #temp4
WHERE List_Price * 10 < 4000 AND Currency = 'SEK'
These queries returning same resultset might have different query plan as per indices created on your table. Use whichever is more efficient than another.
Explore Operator Precedence and UNION for more details.
Related
I just started learning SQL. I filtered the data I have and accordingly two results came out. I want to collect these results, but whatever I do, it throws an error.
Here is my code:
SELECT TOP (1000) [Sec_id]
,[Prc_date]
,[Mkt_Price]
,[Currency]
,[Pricing_factor]
FROM [EXAMPLE1].[dbo].[market]
WHERE Currency='INR' OR Currency='AUD';
SELECT SUM(Mkt_Price)* Pricing_factor as INR FROM market
where Currency='INR' and Mkt_Price>100 group by Pricing_factor ;
SELECT SUM(Mkt_Price)* Pricing_factor as AUD FROM market
where Currency='AUD' and Mkt_Price>100 group by Pricing_factor ;
SELECT SUM(Mkt_Price)* Pricing_factor as totalll FROM market
where Currency='AUD' or Currency='INR' and Mkt_Price>100 group by Pricing_factor
and I got these results:
How can I collect(SUM) and reflect the resulting data ("INR"," AUD")? (Using SMSS)
SELECT Currency, SUM(Mkt_Price * Pricing_factor) AS Total
FROM market
WHERE Currency IN ('AUD', 'INR') AND Mkt_Price > 100
GROUP BY Currency;
Be careful with operator precedence:
Currency = 'AUD' OR Currency = 'INR' AND Mkt_Price > 100
Currency = 'AUD' OR (Currency = 'INR' AND Mkt_Price) > 100 -- same thing
This is what you really wanted:
(Currency = 'AUD' OR Currency = 'INR') AND Mkt_Price > 100
To combine the totals with a grand total you could try a union:
SELECT Currency, SUM(Mkt_Price * Pricing_factor) AS Total
FROM market
WHERE Currency IN ('AUD', 'INR') AND Mkt_Price > 100
GROUP BY Currency
UNION ALL
SELECT 'All', SUM(Mkt_Price * Pricing_factor) AS Total
FROM market
WHERE Currency IN ('AUD', 'INR') AND Mkt_Price > 100;
One you progress past understanding the basic operations you'll want to check into "grouping sets" and "rollup".
I have two tables: transactions and currency. Transactions table contains transactions:
id date client_id currency amount
2 '2017-07-18' 29 'EURO' 340
3 '2018-08-09' 34 'RUB' 5000
Currency table contains currency exchange rates EURO or USD to RUB - 1st row for example means that 1 EURO = 70 RUB. For weekends the are no values as banks are closed and for calculations I need to use Friday exchange rates:
date currency value
'2017-08-07' 'EURO' 70
'2018-08-07' 'USD' 60
'2018-09-09' 'USD' NULL
So I need to calculate amount spent by every client in RUB. And if possible not use window functions.
I tried to use case when and group by client_id but then I need to consider currency rates every time they made a transaction and I don't know how to provide for that.
select t.*, amount * coalesce((select value
from currency c
where c.currency = t.currency
and c.date <= t.date order by c.date desc limit 1),
1)
from transactions t
Assumes if no currency is found it is RUB so it uses 1 as exchange rate.
You can express this with a lateral join:
select t.*,
t.amount * c.value as rub
from transactions t left join lateral
(select c.*
from currency c
where c.currency = t.currency and
c.value is not null and
c.date <= t.date
order by c.date desc
fetch first 1 row only
) c;
I have one table of data with revenue in multiple currencies (let's call this one TRANSACTION_TABLE, with columns as such:
TRANSACTION_NAME
TRANSACTION_VALUE
CURRENCY
, and another table with exchange rates (EXCHANGE_RATE) with columns as such:
FROM_CURRENCY (e.g. JPY)
TO_CURRENCY (e.g. USD)
EXCHANGE_RATE (x)
The table has, at minimum, every currency converting to USD, but is not exhaustive with exchange rates for non-USD TO_CURRENCY values.
What I'm trying to achieve, is a query which converts the transactions to any currency, even if not explicitly stipulated in the EXCHANGE_RATE table, by converting the currencies to USD first, and then from USD into the destination currency.
E.g. 1000 JPY to GBP:
Find rate JPY to USD - calculation = 1000 * EXCHANGE_RATE = 9
Find rate GBP to USD - calculation = 9 \ EXCHANGE_RATE = 7
At the moment, I've done a left join for TRANSACTION_TABLE on EXCHANGE_RATE but I'm lost at where to go next.
Any assistance would be greatly appreciated.
The query (very basic) I've built so far is as follows, and I'm a novice SQL user. I built this query first to convert to USD, which works fine (as my Exchange Rate table contains values for all currencies to USD) - but it obviously fails when setting the destination currency as GBP, as it'll just return nulls.
SELECT TRANSACTION_NAME,
SUM (TRANSACTION_VALUE * EXCHANGE_RATE)
AS "REVENUE GBP"
FROM TRANSACTION_TABLE S
LEFT JOIN EXCHANGE_RATE C ON S.CURRENCY = C.FROM_CURRENCY AND C.TO_CURRENCY = 'GBP'
ORDER BY TRANSACTION_NAME
If your EXCHANGE_RATE table is exhaustive to USD, then you won't ever have more than two "hops" to do your conversion. At most, you'll convert to USD and then from USD to whatever. Given that, I would just code for all the possible cases rather than try something fancy like a CONNECT BY.
"All possible cases", I think, are:
The transaction is already in the target currency
The transaction is in a currency that is directly convertible to the target currency
The transaction must be converted to USD and then from USD to the target currency.
Here is a query that will do that. The WITH clauses are just to give it some data -- they won't be part of your solution, since you have the actual tables.
WITH rates ( from_currency, to_currency, exchange_rate ) AS
( SELECT 'JPY', 'USD', 0.009 FROM DUAL UNION ALL
SELECT 'GBP', 'USD', 1.31 FROM DUAL UNION ALL
SELECT 'CNY', 'USD', 0.15 FROM DUAL UNION ALL
SELECT 'JPY', 'CNY', 0.06 FROM DUAL),
txns ( transaction_name, transaction_value, currency ) AS
( SELECT 'txn 1 in JPY', 1000, 'JPY' FROM DUAL UNION ALL
SELECT 'txn 2 in GBP', 1000, 'GBP' FROM DUAL UNION ALL
SELECT 'txn 3 IN CNY', 1000, 'CNY' FROM DUAL UNION ALL
SELECT 'txn 4 IN unknown', 1000, 'XXX' FROM DUAL),
params ( target_currency ) AS
( SELECT 'CNY' FROM DUAL )
SELECT t.transaction_name,
t.transaction_value base_value,
t.currency base_currency,
t.transaction_value * CASE WHEN t.currency = params.target_currency THEN 1
WHEN r1.from_currency IS NOT NULL THEN r1.exchange_rate
ELSE r2usd.exchange_rate / r2tar.exchange_rate END converted_value,
params.target_currency converted_currency
FROM params CROSS JOIN
txns t
LEFT JOIN rates r1 ON r1.from_currency = t.currency AND r1.to_currency = params.target_currency
LEFT JOIN rates r2usd ON r2usd.from_currency = t.currency AND r2usd.to_currency = 'USD'
LEFT JOIN rates r2tar ON r2tar.from_currency = params.target_currency AND r2tar.to_currency = 'USD'
I'd propose to make an extra step to expand you exchange table with the exchange rates additionaly defined using UDS as transfer currency.
This query adds the new rates calulated via USD. It is a simple inner join constrained so that the calculation is via 'USD' and the from and to currencies are different. The WHERE clause limits the already know combinations.
select er1.FROM_CURRENCY, er2.TO_CURRENCY, er1.EXCHANGE_RATE * er2.EXCHANGE_RATE EXCHANGE_RATE
from exchange_rates er1
join exchange_rates er2
on er1.TO_CURRENCY = 'USD' and er2.FROM_CURRENCY = 'USD' and er1.FROM_CURRENCY != er2.TO_CURRENCY
where (er1.FROM_CURRENCY, er2.TO_CURRENCY)
not in (select FROM_CURRENCY, TO_CURRENCY from exchange_rates)
You may define a physical new table or view or even perform it only as a subquery as an UNION ALL of your original table and the result of this query.
Your final query uses this extended exchange rate table instead of the original one.
Here are sample data I tested with
create table exchange_rates
as
select 'GBP' FROM_CURRENCY, 'USD' TO_CURRENCY, 1.31 EXCHANGE_RATE from dual union all
select 'EUR' FROM_CURRENCY, 'USD' TO_CURRENCY, 1.16 EXCHANGE_RATE from dual union all
select 'AUD' FROM_CURRENCY, 'USD' TO_CURRENCY, .73 EXCHANGE_RATE from dual union all
select 'USD' FROM_CURRENCY, 'GBP' TO_CURRENCY, .76 EXCHANGE_RATE from dual union all
select 'USD' FROM_CURRENCY, 'EUR' TO_CURRENCY, .86 EXCHANGE_RATE from dual union all
select 'USD' FROM_CURRENCY, 'AUD' TO_CURRENCY, 1.36 EXCHANGE_RATE from dual union all
select 'GBP' FROM_CURRENCY, 'EUR' TO_CURRENCY, 1.12 EXCHANGE_RATE from dual;
I'm trying to write a Select statement that increments a column value by 50, but the range can end up being 200,000 so I can't do it all in a case statement manually.
Something similar to this, but instead of manually writing the increments
Select count(order_id) as order_count,
case when revenue between 0 and 50 then ‘$50’
when order_value between 51 and 100 then ‘$100’
else ‘over $101’
end as revenue_bucket
from Orders
group by 2
Turn your revenue into the bucket value, then make string out of it:
SELECT count(order_id) AS order_count,
'$' || ((((revenue - 0.01)/50)::int + 1) * 50)::text AS revenue_bucket
FROM Orders
GROUP BY 2;
This obviously runs well past $200,000.
You can work with modulo to get this. Limit would be 101 in your example. All you have to do, is cast the result in a string and add the $ before it
Select count(order_id) as order_count,
case when revenue < limit then revenue - (revenue % 50) + 50
else ‘over $101’
end as revenue_bucket
from Orders
group by 2
You can round to the nearest 50 with div (integer division):
revenue div 50 * 50
To round up instead of down:
(revenue div 50 + 1) * 50
To include 50 in the next bracket (so 50 as $50 instead of $100):
((revenue-1) div 50 + 1) * 50
Example query:
select revenue
, concat('$', ((revenue-1) div 50 + 1) * 50) as revenue_bucket
from YourTable
See it working at SQL Fiddle.
figured out something similar
select floor((revenue+49)/50)*50 as revenue_bucket,
count(1) as order_count
from Orders
group by 1;
I have a database table that records transactions in a shop. The main data recorded is the cost of each item. E.g.
Item || Cost
TV || 80.00
XboxGame || 55.00
Monitor || 45.00
Controller || 15.00
I want to find out the number of items purchased that cost less than $25, how many cost between $25 and $49.99, and how many cost $50 and above. This should be the result of running the sql command:
Table1 (Target):
Cost || Number of Items
==================================
Less than $25 || 1
$25 - $49.99 || 1
$50 and above || 2
I have tried the following:
SELECT (SELECT COUNT(*) AS Expr1
FROM tblTransactions
WHERE (Cost < 25)) AS [Less than $25],
(SELECT COUNT(*) AS Expr1
FROM tblTransactions AS tblTransactions_1
WHERE (Cost >= 25) AND (Cost < 50)) AS [$25 - $49.99],
(SELECT COUNT(*) AS Expr1
FROM tblTransactions AS tblTransactolions_2
WHERE (ContributionAmount >= 50)) AS [$50 and above];
However this produces:
Table 2 (actual result)
Less than $25 || $25 - $49.99 || $50 and above
========================================
1 || 1 || 2
My main question is: how do I change my SQL so that it produces Table 1, instead of Table 2?
Use Group By:
Select
[Cost] = CASE WHEN cost < 25 then 'Less than $25'
WHEN cost >= 25 and cost < 50 then '$25 - $49.99'
WHEN cost >= 50 then '$50 and above' END
,COUNT(*) As [Count]
FROM tblTransactions
GROUP BY (CASE WHEN cost < 25 then 'Less than $25'
WHEN cost >= 25 and cost < 50 then '$25 - $49.99'
WHEN cost >= 50 then '$50 and above' END)
ORDER BY CASE WHEN
cost < 25 then '1'
WHEN cost >= 25 and cost < 50 then '2'
WHEN cost >= 50 then '3' END
Edited to include a sorting.
You need to have separate queries that are unioned together:
SELECT 'Less than $25' AS [Cost], COUNT(*) AS [Number of Items]
FROM tblTransactions
WHERE (Cost < 25)
UNION ALL
SELECT '$25 - $49.99', COUNT(*)
FROM tblTransactions AS tblTransactions_1
WHERE (Cost >= 25) AND (Cost < 50)
UNION ALL
SELECT '$50 and above', COUNT(*)
FROM Transactions AS tblCoachBudgetHistory_2
WHERE (ContributionAmount >= 50)
Use UNION ALL:
SELECT 'Some text' AS [Label],
COUNT(*)
FROM TABLE
WHERE SomeCol = 'some val'
UNION ALL
SELECT 'Some text' AS [Label],
COUNT(*)
FROM TABLE
WHERE SomeCol = 'some val'
You could do that with a UNION
(SELECT COUNT(*) AS Count,
, "Less than $25" AS Label
FROM tblTransactions
WHERE Cost < 25)
UNION ALL
(SELECT COUNT(*) AS Count
, "$25 - $49.99" as Label
FROM tblTransactions
WHERE (Cost >= 25) AND (Cost < 50))
UNION ALL
(SELECT COUNT(*) AS Count
,"$50 and above" as Label
FROM Transactions
WHERE ContributionAmount >= 50)
If I were writing this, I would start with a subquery (or view) to first group the values into buckets.
SELECT
(CASE WHEN (Cost >= 50) THEN 50
WHEN (Cost >= 25) THEN 25
ELSE 0 END) as costLevel
FROM transactions t
Then it's trivial to group.
SELECT
(CASE costLevel WHEN 50 THEN '$50 and above'
WHEN 25 THEN '$25 - $49.99'
ELSE 'Less than $25' END) as [Cost]
, COUNT(*) AS [Number of Items]
FROM (thePreviousQuery) AS cl
GROUP BY costLevel
-- optional:
-- ORDER BY costLevel
The above syntax is for SQL Server, so YMMV elsewhere. (I've also treated tblTransactions and Transactions as a typo.)
Personally, I tend to prefer range tables for this;
SELECT name, COUNT(*)
FROM (VALUES ('Less than $25', CAST(0 as DECIMAL(5, 2)), CAST(25 as DECIMAL(5, 2))),
('$25 - $49.99', 25, 50),
('$50 and above', 50, null)) Range(name, lower, upper)
LEFT JOIN Transactions
ON cost >= lower
AND (upper IS NULL OR cost < upper)
GROUP BY name
(and working fiddle example. Should work on any RDBMS, pretty much)
This has the added benefit that the table can actually be persisted, meaning maintainable by end-users. Also, an index on cost can be used by the optimizer, meaning potentially better performant queries. Due to the LEFT JOIN, you'll also get a 0 count for ranges that have no transactions.