I have created an SQl Query that gives me the results below
Product| Qty.Sold | Total Value
----------------------------
Men | 585 | 4750
Wom | 927 | 9235
Child | 587 | 6023
I need to create 2 additional columns next to Total Value.
For the new column 1
Formula is first value of Qty.Sold/Sum of Qty Sold.The same applies for corresponding rows(Wom,Child..)
Eg : 585/2561,927/2561....
For the new column 2
Formula is first value of Total Value/Sum of Total Value.The same applies for corresponding rows(Wom,Child..)
Eg : 4750/25726,9235/25726...
How Can I write a sql query to accommodate this new change
So something like this:
SELECT Product,Qty_sold,total_value,
Qty_sold/(select sum(Qty_sold) FROM (Your query here)) as Col1,
total_value/(select sum(total_value) FROM (Your query here)) as Col2
FROM (Your query here)
I assume you want something like this:
SELECT product, qty, value,
qty/(SELECT SUM(qty) FROM tbl) AS qty_ratio
value/(SELECT SUM(value) FROM tbl) AS value_ratio
FROM tbl
Related
I need to calculate the Average value of fields, but two things needs to happen:
1- The empty values should NOT be counted for the average math.
2- If the field is empty it still must be shown in the result (with avg === 0)
Imagine that I have this dataset:
-----------------------
Code | valField | Date
-----------------------
A | | 2020-09-08
B | 12 | 2020-09-09
A | 10 | 2020-09-08
B | 15 | 2020-09-09
B | | 2020-09-09
C | | 2020-09-09
So I need the average of the day. As you can see, we have:
A = { empty, 10 }
B = { 12, 15, empty }
C = { empty }
I need to make the average like this:
Average of A = 10
Average of B = (12+15)/2 (because we have 2 non-empty values)
Average of C = 0 (It has not a single value, but I need it to show on result as 0)
So far I could accomplish both of the requirements, but not in the same time.
This query will show empty values BUT will also count empty fields on average math
SELECT AVG(valField) FROM myTable;
So Average of B would be = (12+15+0)/3 - wrong!
Now this will ignore empty values, the AVG math will be correct, but C would NOT be shown.
SELECT AVG(valFIeld) FROM myTable WHERE valField <> ''
How may I accomplish both requirements?
From your comment I understood, you have valField defined as varchar, so you can use next trick:
select
Code,
coalesce(avg(nullif(valField, '')), 0) as avg_value
from tbl
group by Code;
Test the query on SQLize.online
Here I used NULLIF function for convert empty values to null before calculate the average
I think you want:
SELECT code, COALESCE(AVG(valField), 0) FROM myTable GROUP BY code
This assumes valField is of a numeric datatype, and that by empty you mean null.
Here is what happens behind the hood:
avg(), as most other aggregate functions, ignores null values
if all values are null, then avg() does return null; you can replace that with 0 using coalesce()
That should be easy just create two queries one that calculates the average using non null values and the other one calculating the codes having no value in the data.
select round(avg(valField)) as avg, code from new where valField is not null group by Code
union all
select 0 as avg, code from new group by Code having avg(valField) is null;
I've been searching through StackOverflow and the closest one that I've found is to use custom variable inside subquery. But the suggested solution has two shortcomings.
Table
+----+-------+-------------+
| id | type | MyAmountCol |
+----+-------+-------------+
| 1 | 85482 | 10 |
+----+-------+-------------+
| 2 | 47228 | 20 |
+----+-------+-------------+
| 3 | 12026 | 40 |
+----+-------+-------------+
When every row has cannot meet the condition, (i.e. if every value is larger than the threshold value.) no row is returned.
Example Fiddle
Query
SET #runningTotal=0;
SELECT
O.Id,
O.Type,
O.MyAmountCol,
#runningTotal + O.MyAmountCol as 'RunningTotal',
#runningTotal := #runningTotal + O.MyAmountCol
FROM Table1 O
HAVING RunningTotal <=5;
Returned
0 Row(s)
When the condition is caught in the middle, (i.e. if the first two values are 10 and 20, and the threshold is 15) the sum of returned values are always less than or equal to the threshold.
Example Fiddle
Query
SET #runningTotal=0;
SELECT
O.Id,
O.Type,
O.MyAmountCol,
#runningTotal + O.MyAmountCol as 'RunningTotal',
#runningTotal := #runningTotal + O.MyAmountCol
FROM Table1 O
HAVING RunningTotal <=15;
Returned
1 85482 10 10 (1 Row)
The desired result is this. In the first example fiddle, I want the first row (id=1, type=85842) returned. In the second example fiddle, I want rows with id=1 , type=85842 and id=2, type=47228 returned.
Putting it differently, what I'm trying to do is slightly different from what I've found and it doesn't seem to achieve it with that approach. I want the fewest number of sequential rows that exceed the target value. Is there any way for this with only MySQL (query), or should I solve this in the application level?
JOIN the table to itself on lesser ids (because that's what you're ordering on), summing all the rows from the join and keeping those rows whose sum is less than (not less than or equal) the threshold:
SELECT
a.Id,
a.Type,
a.MyAmountCol
FROM Table1 a
LEFT JOIN Table1 b on b.id < a.id
GROUP BY 1,2,3
HAVING COALESCE(SUM(b.MyAmountCol), 0) < 15
The COALESCE() call is added to cater for the lowest id, which we want to keep (always), having no rows to join with.
Disclaimer: Code may not compile or work as it was thumbed in on my phone (but there's a reasonable chance it will work)
I have a table that for an ID, will have data in several bucket fields. I want a function to pull out a sum of buckets, but the function parameters will include the start and end bucket field.
So, if I had a table like this:
ID Bucket0 Bucket30 Bucket60 Bucket90 Bucket120
10 5.00 12.00 10.00 0.0 8.00
If I send in the ID and the parameters Bucket0, Bucket0, it would return only the value in the Bucket0 field: 5.00
If I send in the ID and the parameters Bucket30, Bucket120, it would return the sum of the buckets from 30 to 120, or (12+10+0+8) 30.00.
Is there a nicer way to write this other than a huge ugly
if parameter1=bucket0 and parameter2=bucket0
then select bucket0
else if parameter1=bucket0 and parameter2=bucket1
then select bucket0 + bucket1
else if parameter1=bucket0 and parameter2=bucket2
then select bucket0 + bucket1 + bucket2
and so on?
The table already exists, so I don't have a lot of control over that. I can make my parameters for the function however I want. I can safely say that if a set of buckets are wanted, none in the middle will be skipped, so specifying start and end buckets would work. I could have a single comma delimited string of all buckets wanted.
It would have been better if your table had been normalised, like this:
id | bucket | value
---+-----------+------
10 | bucket000 | 5
10 | bucket030 | 12
10 | bucket060 | 10
10 | bucket090 | 0
10 | bucket120 | 8
Also, the buckets should better have names that are easy to compare in ranges, so that bucket030 comes between bucket000 and bucket120 in the normal alphabetical order, which is not the case if you leave out the padded zeroes.
If the above normalisation is not possible, then use an unpivot clause to turn your current table into the structure depicted above:
select id, sum(value)
from (
select *
from mytable
unpivot (value for bucket_id in (bucket0 as 'bucket000',
bucket30 as 'bucket030',
bucket60 as 'bucket060',
bucket90 as 'bucket090',
bucket120 as 'bucket120'))
) normalised
where bucket_id between 'bucket000' and 'bucket060'
group by id
When you do this with parameter variables, make sure those parameters have the padded zeroes as well.
You could for instance ensure that as follows for parameter1:
if parameter1 like 'bucket%' then
parameter1 := 'bucket' || lpad(+substr(parameter1, 7), 3, '0');
end if;
...etc.
I have this query :
select
name
from
provinces
WHERE
province_id IN(1,3,2,1)
ORDER BY FIELD(province_id, 1,3,2,1)
the Number of values in IN() are dynamic
How can I get all rows even duplicates ( in this example -> 1 ) with given ORDER BY ?
the result should be like this :
name1
name3
name2
name1
plus I shouldn't use UNION ALL :
select * from provinces WHERE province_id=1
UNION ALL
select * from provinces WHERE province_id=3
UNION ALL
select * from provinces WHERE province_id=2
UNION ALL
select * from provinces WHERE province_id=1
You need a helper table here. On SQL Server that can be something like:
SELECT name
FROM (Values (1),(3),(2),(1)) As list (id) --< List of values to join to as a table
INNER JOIN provinces ON province_id = list.id
Update: In MySQL Split Comma Separated String Into Temp Table can be used to split string parameter into a helper table.
To get the same row more than once you need to join in another table. I suggest to create, only once(!), a helper table. This table will just contain a series of natural numbers (1, 2, 3, 4, ... etc). Such a table can be useful for many other purposes.
Here is the script to create it:
create table seq (num int);
insert into seq values (1),(2),(3),(4),(5),(6),(7),(8);
insert into seq select num+8 from seq;
insert into seq select num+16 from seq;
insert into seq select num+32 from seq;
insert into seq select num+64 from seq;
/* continue doubling the number of records until you feel you have enough */
For the task at hand it is not necessary to add many records, as you only need to make sure you never have more repetitions in your in condition than in the above seq table. I guess 128 will be good enough, but feel free to double the number of records a few times more.
Once you have the above, you can write queries like this:
select province_id,
name,
#pos := instr(#in2 := insert(#in2, #pos+1, 1, '#'),
concat(',',province_id,',')) ord
from (select #in := '0,1,2,3,1,0', #in2 := #in, #pos := 10000) init
inner join provinces
on find_in_set(province_id, #in)
inner join seq
on num <= length(replace(#in, concat(',',province_id,','),
concat(',+',province_id,',')))-length(#in)
order by ord asc
Output for the sample data and sample in list:
| province_id | name | ord |
|-------------|--------|-----|
| 1 | name 1 | 2 |
| 2 | name 2 | 4 |
| 3 | name 3 | 6 |
| 1 | name 1 | 8 |
SQL Fiddle
How it works
You need to put the list of values in the assignment to the variable #in. For it to work, every valid id must be wrapped between commas, so that is why there is a dummy zero at the start and the end.
By joining in the seq table the result set can grow. The number of records joined in from seq for a particular provinces record is equal to the number of occurrences of the corresponding province_id in the list #in.
There is no out-of-the-box function to count the number of such occurrences, so the expression at the right of num <= may look a bit complex. But it just adds a character for every match in #in and checks how much the length grows by that action. That growth is the number of occurrences.
In the select clause the position of the province_id in the #in list is returned and used to order the result set, so it corresponds to the order in the #in list. In fact, the position is taken with reference to #in2, which is a copy of #in, but is allowed to change:
While this #pos is being calculated, the number at the previous found #pos in #in2 is destroyed with a # character, so the same province_id cannot be found again at the same position.
Its unclear exactly what you are wanting, but here's why its not working the way you want. The IN keyword is shorthand for creating a statement like ....Where province_id = 1 OR province_id = 2 OR province_id = 3 OR province_id = 1. Since province_id = 1 is evaluated as true at the beginning of that statement, it doesn't matter that it is included again later, it is already true. This has no bearing on whether the result returns a duplicate.
How do i calculate the percentage from the same column, between two dates?
I have column latest.
For example: 2015-11-16 the value was 159,4 and today the value is 160,1.
And between these dates there is other values, that i´m not interested in at the moment. How to i calculate the percentage difference from that specific date, compared to "todays" date.
EDIT
SELECT curr.latest * 100 / NULLIF(prev.latest, 0) as percentage
FROM myTable AS curr, myTable AS prev
WHERE date(prev.timestamp) = '2015-11-16'
AND date(curr.timestamp) = CURDATE()
AND curr.the_row_namn = 'apple'
Percentage of column latest of a specific date ex. 2015-11-16, with the the_row_namn of apple from the table myTable.
What is the percentage difference of column latest for apple of the day 2015-11-16 to today.
+--------------+--------+------------+
| the_row_namn | latest | timestamp |
+--------------+--------+------------+
| apple | 159,40 | 2015-11-16 |
| apple | 164,1 | 2015-11-17 |
+--------------+--------+------------+
Expected output: (rounded) percentage: 0,2864 or even better if that is possible 2,8%
Hope this will clarify things.
You could do this with the following SELECT statement:
SELECT curr.name,
curr.value * 100 / NULLIF(prev.value, 0) as percentage
FROM myTable AS curr
INNER JOIN myTable AS prev
ON curr.name = prev.name
WHERE prev.latest = '2015-10-26'
AND curr.latest = CURDATE()
AND curr.name = 'apple';
If you leave out the last condition, you'll get a result per possible value of name.
The main point of this query is that you use your table twice, once to select the current record, and once to retrieve the earlier record. You should just specify the date of your choice in the first line of the where clause.
Once you have both records, it is straightforward to calculate a percentage from the value columns of these two records.
The NULLIF is there to protect you from division by zero errors. Instead you'll get a NULL result.
This is the general form such a query can take:
SELECT a.value / b.value AS abPerc
FROM the_table AS a
INNER JOIN the_table AS b ON [pairing criteria]
;