count based on store_A and all stores - sql

I would like to count:
all count_products for store_A
the total count_products which includes all store_id's even store_A as total_count_products
Main_table
date store_id count_prroducts
2019-01-01 A 13
2019-01-01 B 34
2019-01-01 C 63
2019-01-01 D 10
Output_table
date store_A_count_products total_count_products
2019-01-0 13 120

Start out by selecting the date column without any modifications.
For store_A_count_products, basically what you need to do is add up all of the count_products whenever the store_id is A. You can do this with a case statement:
case when store_id = 'A' then count_products else 0 end
This is basically an IF/ELSE situation and will return a 0 for any row that doesn't have A in the store_id column.
If you wrap that up in a SUM(), you will add all the rows together.
For total_count_products, you just need to wrap a SUM() around count_products. This will add up all rows regardless of the status of any other column.
Finally, you need to group by the date column. The group by is a means to split the aggregated data across unaggregated columns.
The reason this works is because it gives you one row for each date, the summed total of products for Store A and the summed total of all products.
Select
date,
Sum(case when store_id = 'A' then count_products else 0 end) as store_A_count_products,
SUM(count_products) as total_count_products
From main_table
Group by date;

Related

SQL SUM and value conversion

I'm looking to transform data in my SUM query to acknowledge that some numeric values are negative in nature, although not represented as such.
I look for customer balance where the example dataset includes also credit transactions that are not written as negative in the database (although all records that have value C for credit in inv_type column should be treated as negative in the SQL SUM function). As an example:
INVOICES
inv_no inv_type cust_no value
1 D 25 10
2 D 35 30
3 C 25 5
4 D 25 50
5 C 35 2
My simple SUM function would not give me the correct answer:
select cust_no, sum(value) from INVOICES
group by cust_no
This query would obviously sum the balance of customer no 25 for 65 and no 35 for 32, although the anticipated answer would be 10-5+50 = 55 and 30 - 2 = 28
Should I perhaps utilize CAST function somehow? Unfortunately I'm not up to date on the underlying db engine, however good chance of it being of IBM origin. Most of the basic SQL code has worked out so far though.
You can use the case expression inside of a sum(). The simplest syntax would be:
select cust_no,
sum(case when inv_type = 'C' then - value else value end) as total
from invoices
group by cust_no;
Note that value could be a reserved word in your database, so you might need to escape the column name.
You should be able to write a projection (select) first to obtain a signed value column based on inv_type or whatever, and then do a sum over that.
Like this:
select cust_no, sum(value) from (
select cust_no
, case when inv_type='D' then [value] else -[value] end [value]
from INVOICES
) SUMS
group by cust_no
You can put an expression in the sum that calculates a negative value if the invoice is a credit:
select
cust_no,
sum
(
case inv_type
when 'C' then -[value]
else [value]
end
) as [Total]
from INVOICES

Calculate percentages of columns in Oracle SQL

I have three columns, all consisting of 1's and 0's. For each of these columns, how can I calculate the percentage of people (one person is one row/ id) who have a 1 in the first column and a 1 in the second or third column in oracle SQL?
For instance:
id marketing_campaign personal_campaign sales
1 1 0 0
2 1 1 0
1 0 1 1
4 0 0 1
So in this case, of all the people who were subjected to a marketing_campaign, 50 percent were subjected to a personal campaign as well, but zero percent is present in sales (no one bought anything).
Ultimately, I want to find out the order in which people get to the sales moment. Do they first go from marketing campaign to a personal campaign and then to sales, or do they buy anyway regardless of these channels.
This is a fictional example, so I realize that in this example there are many other ways to do this, but I hope anyone can help!
The outcome that I'm looking for is something like this:
percentage marketing_campaign/ personal campaign = 50 %
percentage marketing_campaign/sales = 0%
etc (for all the three column combinations)
Use count, sum and case expressions, together with basic arithmetic operators +,/,*
COUNT(*) gives a total count of people in the table
SUM(column) gives a sum of 1 in given column
case expressions make possible to implement more complex conditions
The common pattern is X / COUNT(*) * 100 which is used to calculate a percent of given value ( val / total * 100% )
An example:
SELECT
-- percentage of people that have 1 in marketing_campaign column
SUM( marketing_campaign ) / COUNT(*) * 100 As marketing_campaign_percent,
-- percentage of people that have 1 in sales column
SUM( sales ) / COUNT(*) * 100 As sales_percent,
-- complex condition:
-- percentage of people (one person is one row/ id) who have a 1
-- in the first column and a 1 in the second or third column
COUNT(
CASE WHEN marketing_campaign = 1
AND ( personal_campaign = 1 OR sales = 1 )
THEN 1 END
) / COUNT(*) * 100 As complex_condition_percent
FROM table;
You can get your percentages like this :
SELECT COUNT(*),
ROUND(100*(SUM(personal_campaign) / sum(count(*)) over ()),2) perc_personal_campaign,
ROUND(100*(SUM(sales) / sum(count(*)) over ()),2) perc_sales
FROM (
SELECT ID,
CASE
WHEN SUM(personal_campaign) > 0 THEN 1
ELSE 0
end AS personal_campaign,
CASE
WHEN SUM(sales) > 0 THEN 1
ELSE 0
end AS sales
FROM the_table
WHERE ID IN
(SELECT ID FROM the_table WHERE marketing_campaign = 1)
GROUP BY ID
)
I have a bit overcomplicated things because your data is still unclear to me. The subquery ensures that all duplicates are cleaned up and that you only have for each person a 1 or 0 in marketing_campaign and sales
About your second question :
Ultimately, I want to find out the order in which people get to the
sales moment. Do they first go from marketing campaign to a personal
campaign and then to sales, or do they buy anyway regardless of these
channels.
This is impossible to do in this state because you don't have in your table, either :
a unique row identifier that would keep the order in which the rows were inserted
a timestamp column that would tell when the rows were inserted.
Without this, the order of rows returned from your table will be unpredictable, or if you prefer, pure random.

Find the proportion of rows verifying a condition in a single SQL query

Suppose I have a sales table which is as follows:
ID | Price
----------------------
1 0.33
2 1.5
3 0.5
4 10
5 0.99
I would like to find, in a single query, the proportion of rows verifying a given condition. For example, if the condition is Price < 1, the result should be 3/5 = 0.6.
The only workaround that I have found so far is :
SELECT
SUM(
CASE
WHEN Price < 1
THEN 1
WHEN Price >= 1
THEN 0
END
)/COUNT(*)
FROM sales
but is there a way to do this without CASE ?
You can do it with IF:
SELECT SUM(IF(Price < 1, 1, 0))/COUNT(*) FROM sales
-but it's no big difference from CASE (your logic here is correct)
You may want to use WHERE (to sum only Price<1) - but since you're using total COUNT it's not valid in your case. Another option: get total count separately:
SELECT
COUNT(sales.Price)/total_count
FROM
sales
CROSS JOIN (SELECT COUNt(*) AS total_count FROM sales) AS c
WHERE
-- you're summing 1 or 0 depending of Price, so your sum is
-- just count where Price<1
sales.Price<1

SQL : return all rows that contain particular value more than once

I have a table with the following contents:
store Qty
----
store1 1
store2 2
store1 3
store2 2
I want to output this:
------------
store Qty
store2 2(value '2' occurs 2 times)
store1 0(value '2' occurs 0 times)
I want to return occurrence of column Qty with value '2' (with how many times it occured) in descending order.
You want conditional aggregation (that is, using a case statement with an aggregation function):
select store, sum(case when Qty = '2' then 1 else 0 end) as Qty
from table t
group by store;
SELECT t.store, COUNT(QTY) AS QTY2
FROM TABLE T
WHERE t.QTY = 2
GROUP BY t.store
ORDER BY COUNT(QTY) DESC
This should work. It will provide a count of how many 2's are present grouped by store and show the stores in descending order based on the number of 2's present.

mysql table inside join

I have table tblsale.
In this table i have field called BillType, which contains "s" and "r" (s = sale , r = returns )
The table has rougly 25 records. Of that, 7 records are "r", and rest of the records are "s".
How do I write the query so that my result set includes the following columns:
What is want exactly is below
Amount BillType Amount BillType Date
100 s 50 r 29-11-2010
120 s 20 r 28-11-2010
130 s 30 r 27-11-2010
140 s 50 r 26-11-2010
What you appear to want are the results of two queries, sales and returns, side by side. It can be done with a kludge like this:
select amount, sale, returnamount, returned, returndate
from
(
select amount, 1 as sale, 0 as returnamount, 0 as returned, '' as returndate
from sales where billtype='s'
union
select 0 as amount, 0 as sale, amount as returnamount, 1 as returned, date as returndate
from sales where billtype ='r'
)
You may have to cast date into a string representation. The unioned sets need the same column structure, so you create dummy columns. (You didn't ask for a sale date for sales.)
Or you can do this with CASE WHEN statement.
I'm not sure what you are asking, but maybe:
SELECT BillNo, VAT, BillType, AfterDiscount FROM tblsale WHERE BillType = 's';