I have a very simple table filled with about 500k records
| ID_1 | ID_2 | Date | Fields... |
+-------+-------+-------------+-----------+
| 1 | 1 | 1/1/2016 | abc |
| 2 | 1 | 2/1/2016 | abc |
| 3 | 2 | 1/2/2016 | abc |
| 4 | 3 | 1/2/2016 | abc |
| 5 | 4 | 1/2/2016 | abc |
I have to extract from that 36 values based on common parameters and differentiated by the date.
To reduce accesses to the database and increase performance I do a subquery, and then sum the results differentiating them with an if.
The not distinct sums work perfectly fine, but I need to count some distinct value and this returns me the correct value + 1.
SELECT
SUM( IF(MONTH(Date) = 1, 1, 0) ) AS A1,
SUM( IF(MONTH(Date) = 2, 1, 0) ) AS A2,
SUM( IF(MONTH(Date) = 3, 1, 0) ) AS A3,
...
COUNT(DISTINCT( IF(MONTH(Date) = '1', COALESCE(ID_cliente), 0) )) AS C1
FROM (
SELECT ID_1, ID_2, Date FROM table WHERE Fields = conditions
) AS Sub
The C1 returns 1 if the real value is 0, 2 if the real value is 1 etc
There's a solution or I must split it into two queries?
Related
Suppose you have a a multi-event competition where competitors can attempt any event an arbitrary number of times. (weird, I know.)
How do pull out a desired player's best time for each event,
and assign it a placing? (1st 2nd 3rd...)
Data example: Desired output:
Name | Event | Score Name | Event | Score | Rank
-------------------- ----------------------------
Bob 1 50 Given input: "Bob"
Bob 1 100 Bob 1 100 1
Bob 2 75 Bob 2 75 3
Bob 3 80 Bob 3 80 2
Bob 3 65
Given input: "Jill"
Jill 2 75 Jill 2 90 1
Jill 2 90 Jill 3 60 3
Jill 3 60
Given input: "Chris"
Chris 1 70 Chris 1 70 2
Chris 2 50 Chris 2 85 2
Chris 2 85 Chris 3 100 1
Chris 3 100
This is a build up of my previous question:
Multi-event tournament standings
I feel understand that problem much better (Thanks!), but I cannot bridge the gap to this version of the problem.
I have SQL 5.x so I cant use stuff like Rank(). This will also be crunching many thousands of scores.
Desired output can be acheaved with this query:
select
IF(event is NULL, CONCAT('Given input: "', name,'"'), name) as name,
IF(event is NULL, '', event) as event,
IF(event is NULL, '', max(score)) as score,
IF(event is NULL, '', (
select count(s2.name) + 1
from (
select name, max(score) as score
from scores es
where es.event = s.event
group by es.name
order by score desc
) s2
where s2.score > max(s.score)
)) as `rank`
from scores s
group by name, event with rollup
having name is not NULL
order by name, event;
And output (if run query in mysql cli):
+----------------------+-------+-------+------+
| name | event | score | rank |
+----------------------+-------+-------+------+
| Given input: "Bob" | | | |
| Bob | 1 | 100 | 1 |
| Bob | 2 | 75 | 3 |
| Bob | 3 | 80 | 2 |
| Given input: "Chris" | | | |
| Chris | 1 | 70 | 2 |
| Chris | 2 | 85 | 2 |
| Chris | 3 | 100 | 1 |
| Given input: "Jill" | | | |
| Jill | 2 | 90 | 1 |
| Jill | 3 | 60 | 3 |
+----------------------+-------+-------+------+
11 rows in set, 3 warnings (0.00 sec)
Should work on any Mysql 5.
You can get the highest score per event by an aggregation by event taking the max(). To simulate a dense_rank() you can use a subquery counting the scores higher than or equal to the current score per event.
For a particular contestant (here Bob) that makes:
SELECT d1.name,
d1.event,
max(d1.score) score,
(SELECT count(*)
FROM (SELECT d2.event,
max(d2.score) score
FROM data d2
GROUP BY d2.event,
d2.name) x1
WHERE x1.score >= max(d1.score)
AND x1.event = d1.event) rank
FROM data d1
WHERE d1.name = 'Bob'
GROUP BY d1.event
ORDER BY d1.event;
And for all of them at once:
SELECT d1.name,
d1.event,
max(d1.score) score,
(SELECT count(*)
FROM (SELECT d2.event,
max(d2.score) score
FROM data d2
GROUP BY d2.event,
d2.name) x1
WHERE x1.score >= max(d1.score)
AND x1.event = d1.event) rank
FROM data d1
GROUP BY d1.name,
d1.event
ORDER BY d1.name,
d1.event;
db<>fiddle
E.g.:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,name VARCHAR(12) NOT NULL
,event INT NOT NULL
,score INT NOT NULL
);
INSERT INTO my_table (name,event,score) VALUES
('Bob' ,1, 50),
('Bob' ,1,100),
('Bob' ,2, 75),
('Bob' ,3, 80),
('Bob' ,3, 65),
('Jill' ,2, 75),
('Jill' ,2, 90),
('Jill' ,3, 60),
('Chris',1, 70),
('Chris',2, 50),
('Chris',2, 85),
('Chris',3,100);
SELECT a.*
, FIND_IN_SET(a.score,b.scores) my_rank
FROM my_table a -- it's possible that this really needs to be a repeat of the subquery below, so
-- ( SELECT m.* FROM my_table m JOIN (SELECT name,event,MAX(score) score FROM my_table
-- GROUP BY name, event) n ON n.name = m.name AND n.event = m.event AND n.score = m.score) AS a
JOIN
(
SELECT x.event
, GROUP_CONCAT(DISTINCT x.score ORDER BY x.score DESC) scores
FROM my_table x
JOIN
( SELECT name
, event
, MAX(score) score
FROM my_table
GROUP
BY name
, event
) y
ON y.name = x.name
AND y.event = x.event
AND y.score = x.score
GROUP
BY x.event
) b
ON b.event = a.event
WHERE FIND_IN_SET(a.score,b.scores) >0;
+----+-------+-------+-------+------+
| id | name | event | score | rank |
+----+-------+-------+-------+------+
| 2 | Bob | 1 | 100 | 1 |
| 3 | Bob | 2 | 75 | 3 |
| 4 | Bob | 3 | 80 | 2 |
| 6 | Jill | 2 | 75 | 3 |
| 7 | Jill | 2 | 90 | 1 |
| 8 | Jill | 3 | 60 | 3 |
| 9 | Chris | 1 | 70 | 2 |
| 11 | Chris | 2 | 85 | 2 |
| 12 | Chris | 3 | 100 | 1 |
+----+-------+-------+-------+------+
Please help me.
Here is the raw data:
uid | id | value |
1 | a | 389 |
2 | b | 201 |
3 | c | 170 |
if.... When the reference value is '200'
How do you get it to come out like this?
mysql..
no| uid | id | value | cut
1 | 1 | a | 200 | 200
2 | 1 | a | 189 | 200
3 | 2 | b | 200 | 200
4 | 2 | b | 1 | 200
5 | 3 | c | 170 | 200
help me!!!!
If you are using MySQL 8 or later, Recursive CTEs can help:
CREATE DATABASE test;
USE test;
CREATE TABLE TestData (uid INTEGER, id VARCHAR(8), value INTEGER);
INSERT INTO TestData VALUES (1, 'a', 389);
INSERT INTO TestData VALUES (2, 'b', 201);
INSERT INTO TestData VALUES (3, 'c', 170);
INSERT INTO TestData VALUES (4, 'd', 550);
-- Set up an auto-incrementing row number
SET #row_num = 0;
WITH RECURSIVE cte (uid, id, value, remainder) AS (
-- start with a copy of the table, but adding another column for the value that is at most 200
SELECT a.uid, a.id, LEAST(a.value, 200), a.value AS "remainder" FROM TestData a
UNION
-- repeatedly select from the previous result set, meanwhile decrementing the "remainder" column
SELECT uid, id, LEAST(remainder - 200, 200), remainder - 200 FROM cte WHERE remainder > 200
)
-- select the actual data that we care about
SELECT (#row_num := #row_num + 1) AS no, uid, id, value, 200 AS "cut" FROM cte ORDER BY id, value DESC;
This results in the following table:
no | uid | id | value | cut
1 | 1 | a | 200 | 200
2 | 1 | a | 189 | 200
3 | 2 | b | 200 | 200
4 | 2 | b | 1 | 200
5 | 3 | c | 170 | 200
6 | 4 | d | 200 | 200
7 | 4 | d | 200 | 200
8 | 4 | d | 150 | 200
How about this:
SET #row_num = 0;
SELECT (#row_num := #row_num + 1) AS "no",uid,id,valueA, "200" as cut FROM
(SELECT uid,id,IF(valueA/200 > 1,200,valueA) AS "valueA" FROM tableA UNION ALL
SELECT uid,id,IF(valueA/200 > 2,200,valueA-200) AS "valueA" FROM tableA UNION ALL
SELECT uid,id,IF(valueA/200 > 3,200,valueA-400) AS "valueA" FROM tableA) a
WHERE valueA > 0
ORDER BY uid,id,valueA DESC;
If valueA divide by 200 equal to more than 1, it will return 200, otherwise it will only return what's in valueA. That's the first syntax among the UNION ALL queries.
The second one I added another condition to as if valueA divided by 200 IS NOT equal to more than 2, it will return valueA-200. You can try to execute the UNION ALL queries to see the results.
It's not the best way to do it and I am sure that there are better solution out there but I got it working using this method. You might use this as temporary solution or at least give you some ideas.
Consider writing a stored procedure instead of doing this all in pure SQL or just select the rows from the table and do the following in php:
From my understanding, you're looking for this:
no = 1
For each row in the result set
for i = 0 to int(value/cut)
output the row with value set to cut
no += 1
output the row with value set to mod(value, cut)
no += 1
Below is the table data (Order_audit)
+------------+-----------------+
| OrderID | shipping_type |
+------------+-----------------+
| W1 | 0 |
| W1 | 2 |
| W2 | 2 |
| W3 | 2 |
| W3 | 2 |
| W3 | 1 |
| W4 | 0 |
| W5 | 1 |
| W5 | 2 |
+------------+-----------------+
I want sql to extract orderID having shipping_type with combinations of (0 or 1) and 2. In this example W1,W3,W5 are orders which fall in this criteria.
Assuming Table Name is order_audit.
Kindly help me on this.
We can try aggregating by OrderID and then asserting the following two conditions on each order group:
There are two (and only two) distinct shipping_type values present
One of those shipping_type values is 2
If both of the above conditions are true, it would imply that the order had either (0, 2) or (1, 2) as shipping combinations.
SELECT OrderID
FROM yourTable
WHERE shipping_type IN (0, 1, 2)
GROUP BY OrderID
HAVING
COUNT(DISTINCT shipping_type) = 2 AND
MAX(CASE WHEN shipping_type = 2 THEN 1 ELSE 0 END) = 1;
Demo
One way to do it would be with this query which looks for all orders which have a shipping_type of 0 or 1 and have a matching entry with a shipping_type of 2.
SELECT OrderID
FROM order_audit a1
WHERE shipping_type IN(0,1) AND
EXISTS (SELECT *
FROM order_audit a2
WHERE a2.OrderID = a1.OrderID AND shipping_type = 2)
Demo
I'm using mysql to storing my chemical analysis. And filtering the results with html/php and to generate the query to fetch results I want. Now things getting complex for me and I'm trying to self join the table to apply all the filters.
My table desing is like that with more than 50k rows.
+--------+---------+-------------+----------------+
| Column | Type | Index | |
+--------+---------+-------------+----------------+
| RID | int | primary_key | auto increment |
| ID | int | index | |
| Type | int | | |
| Order | int | | |
| Num | int | | |
| Val1 | decimal | | |
| Val2 | decimal | | |
+--------+---------+-------------+----------------+
Every sample has an ID, different types and order. Val1 and Val2 are the results of Num type analyse. There 42 different Num by now.
For example If my filters are like that,
Select Types (1,3,9)
Select ANum (0,5)
Type 1, Num 5 should be bigger than 10 In Val2
Type 1, Num 5, should be smaller than 30 In Val2
Type 3, Num 0, should be smaller than 8 In Val1
I'm using that query to produce results.
SELECT analyse.* FROM analyse
INNER JOIN
(SELECT ID FROM analyse WHERE Type = 1 AND Num = 5 AND Val2 < 30) AS a1 ON a1.SID = analyse.SID
INNER JOIN
(SELECT SID FROM WHERE Type = 1 AND Num = 5 AND Val2 > 10) AS a2 ON a2.SID = analyse.SID
INNER JOIN
(SELECT SID FROM analyse WHERE Type = 3 AND Num = 0 AND Val1 > 8) AS a3 ON a2.SID = analyse.SID
WHERE Type IN (1,3,9) AND Num IN (0,5) ORDER BY ID, Type, Order, Num ASC
+-----+------+-------+---------------+--------------+--------------+--------------+
| ID | Type | Order | Val1[Num[0]] | Val2[Num[0]] | Val1[Num[5]] | Val2[Num[5]] |
+-----+------+-------+---------------+--------------+--------------+--------------+
| ... | ... | ... | ... | ... | ... | ... |
| 118 | 1 | 1 | 10.9000 | 2.2083 | 3.5056 | 15.2627 |
| 118 | 1 | 2 | 9.5000 | 1.9246 | 2.0305 | 11.7049 |
| 118 | 1 | 3 | 7.9000 | 1.6005 | 2.4274 | 16.6597 |
| 118 | 2 | 1 | 10.9000 | 2.2083 | 3.5056 | 15.2627 |
| 118 | 3 | 2 | 20.4000 | 4.1329 | 2.8187 | 22.9676 |
| 118 | 4 | 3 | 28.3000 | 5.7334 | 2.7094 | 29.6273 |
| 119 | 1 | 1 | 27.2000 | 6.8635 | 0.5506 | 14.9084 |
| 119 | 1 | 2 | 25.9000 | 6.5355 | 0.4249 | 10.9550 |
| 119 | 3 | 1 | 27.2000 | 6.8635 | 0.5506 | 14.9084 |
| 119 | 3 | 2 | 53.1000 | 13.3989 | 0.4893 | 25.8634 |
| ... | ... | ... | ... | ... | ... | ... |
+-----+------+-------+---------------+--------------+--------------+--------------+
Desired output should be like this. However, since there are other Val1 and Val2 values has same ID, same Type but different order, INNER JOIN doesn't work as expected I guess. There are always bigger and smaller values appear than my filter. Some of them never shown.
Sorry If it's too long. I can't find the correct statment or structure. Any advice?
Edit: Sorry. I forgot to mention all other the data must be shown of IDs. That's why I'm trying to use join.
So, your requirement on the table is
Select Types (1,3,9)
Select Num (0,5)
Type 1, Num 5 should be bigger than 10 In Val2
Type 1, Num 5, should be smaller than 30 In Val2
Type 3, Num 0, should be smaller than 8 In Val1
That's basically 4 different criterias which need to be evaluated (Type, num, Val1, Val2)
While there might be "better" solutions (in Terms of performance or easier to read code) the easiest approach is to create distinct pairs of conditions, which should be fine unless you deal with millions of records.
Out of your requirement, I would derrive that you need the following row-sets to be fetched:
1.) Type = 1, Num = 5, Val2 > 10
2.) Type = 1, Num = 5, Val2 < 30
3.) Type = 3, Num = 0, Val1 < 8
4.) Type = 9
So, that would be a union of "4 explicit" queries:
SELECT * FROM analyse WHERE Type = 1 AND Num = 5 AND Val2 > 10
UNION
SELECT * FROM analyse WHERE Type = 1 AND Num = 5 AND Val2 < 10
UNION
SELECT * FROM analyse WHERE Type = 3 AND Num = 0 AND Val1 < 8
UNION
SELECT * FROM analyse WHERE Type = 9
To filter the final result by id, you can simply use:
SELECT * FROM (
SELECT * FROM analyse WHERE Type = 1 AND Num = 5 AND Val2 > 10
UNION
SELECT * FROM analyse WHERE Type = 1 AND Num = 5 AND Val2 < 10
UNION
SELECT * FROM analyse WHERE Type = 3 AND Num = 0 AND Val1 < 8
UNION
SELECT * FROM analyse WHERE Type = 9
) as tempTable WHERE id = 118
or just append the id to each independent query.
I have solved my problem with querying same Nums and Types together instead of fetching them with seperate joins. By that way, no other data for same Num and Type can pass the filter.
For that conditions
Select Types (1,3,9)
Select ANum (0,5)
Type 1, Num 5 should be bigger than 10 In Val2
Type 1, Num 5, should be smaller than 30 In Val2
Type 3, Num 0, should be smaller than 8 In Val1
Query should be this,
SELECT analyse.* FROM analyse
INNER JOIN
(SELECT ID FROM analyse WHERE Type = 1 AND Num = 5 AND Val2 < 30 AND Val2 > 10) AS a1 ON a1.SID = analyse.SID
(SELECT SID FROM analyse WHERE Type = 3 AND Num = 0 AND Val1 > 8) AS a3 ON a2.SID = analyse.SID
WHERE Type IN (1,3,9) AND Num IN (0,5) ORDER BY ID, Type, Order, Num ASC
I have a table which has many properties in Big Query. I am concerned with two properties of the Big Query.
I have
hits.eCommerceAction.action_type , hits.product.productSKU and hits.hitNumber.
Have written the following code
SELECT hits.product.productSKU as SKU,
IF(hits.eCommerceAction.action_type = '1', (hits.hitNumber), NULL) ListCLicks
IF(hits.eCommerceAction.action_type = '2', (hits.hitNumber), NULL)) PDV
FROM [107485206.ga_sessions_20160101]
where hits.eCommerceAction.action_type in ('1')
#group by SKU,ListClicks
My problem is that the above code returns the first value of hits.hitNumber which is a index number for a SKU. A SKU can have multiple hits.hitNumber. I want to count(not sum as it a index) the total hits.hitNumber for SKU.
ProductSKU | PDV | ListCLicks
-------------------------
1 | 120 | 235
2 | 234 | 124
3 | 2311| 1256
4 | 12 | 34
5 | 12 | 33
2 | 112 | 345
4 | 789 | 1110
2 | 3333| 2131
PDV is hits.hitNumber index when hits.eCommerceAction.action_type = '2'and
List Clicks is hits.hitNumber index when hits.eCommerceAction.action_type = '1'
And the Output is
ProductSKU | PDV | ListCLicks
-------------------------
1 | 1 | 1
2 | 3 | 3
3 | 1 | 1
4 | 2 | 2
5 | 1 | 1
PDV is hits.hitNumber count when hits.eCommerceAction.action_type = '2'and
List Clicks is hits.hitNumber count when hits.eCommerceAction.action_type = '1'
How can I do this?
I am assuming you want to count the number of ecommerce actions by type. If so, you can accomplish this with a SUM that adds 1 per relevant action, and 0 otherwise:
SELECT hits.product.productSKU as SKU,
SUM(IF(hits.eCommerceAction.action_type = '1', 1, 0)) ListCLicks,
SUM(IF(hits.eCommerceAction.action_type = '2', 1, 0)) PDV
FROM [107485206.ga_sessions_20160101]
GROUP BY SKU