I have a MySQL table with the following columns :
+----------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| date | varchar(11) | NO | | NULL | |
| time | varchar(12) | NO | | NULL | |
| gmt_offset | varchar(2) | YES | | NULL | |
| type | varchar(10) | YES | | NULL | |
| yield_b | varchar(10) | YES | | NULL | |
| yield_d | varchar(10) | YES | | NULL | |
+----------------+-------------+------+-----+---------+----------------+
And I am trying to keep one row per minute (the first one of every minute) using the following query:
create table temp_table1 as
select t.*
from (select t1.*,
(#rn := if(#prevd <> date or minute(time) <> #prevm, 1,
if(#prevd := date, if(#prevm := minute(time), 1, 1), 1)
)
) as seqnum
from table1 t1 cross join
(select #rn := 0, #prevd := 0, #prevm := 0) vars
order by date, time
) t
where seqnum = 1;
truncate table table1;
insert into table1(col1, . . ., coln)
select col1, . . . , coln
from temp_table1;
I try with this query to enumerate all rows within a minute, and then keep the first row for every minute.. But this doesn't work, it simply puts 1 in seqnum for every row, and does not get rid of any rows at all.
Can anybody help me make this query work and keep the first row of every minute?
Thanks in advance!
I don't know why the logic was inverted in the first if(). I think I was confused. Apologies for that.
create table temp_table1 as
select t.*
from (select t1.*,
(#rn := if(#prevd = date and minute(time) = #prevm, #rn + 1,
if(#prevd := date, if(#prevm := minute(time), 1, 1), 1)
)
) as seqnum
from table1 t1 cross join
(select #rn := 0, #prevd := 0, #prevm := 0) vars
order by date, time
) t
where seqnum = 1;
The logic for the variable assignment in this case is:
First clause does the comparisons and increments the #rn value.
The next clauses assign the new values of the variables using nested if() calls, returning 1 for all possibilities.
The purpose of putting all the assignments in a single expression is not to create arbitrarily complicated SQL. MySQL does not guarantee the order of evaluation of expressions in a select. If you have multiple expressions with variable assignments, then they may evaluated in the wrong order.
Related
Im using user variables to emulate
ROW_NUMBER() OVER (PARTITION BY `wsf_ref`, `type` ORDER BY `wsf_value` DESC)
Pay attention to the #type variable. I set it to a to make the issue clear but at first was an empty string.
CROSS JOIN ( SELECT #rn := 0, #type := 'a', #ref := '') as var
SQL DEMO #1
CREATE TABLE t (
`id` INTEGER,
`wsf_ref` INTEGER,
`status` VARCHAR(8),
`type` VARCHAR(6),
`wsf_progress` VARCHAR(5),
`wsf_value` INTEGER
);
SELECT t.*, #rn := if( #ref = `wsf_ref`,
if ( #type = `type`,
#rn + 1,
if( #type := `type`, 1, 1)
),
if ( (#ref := `wsf_ref`) and (#type := `type`), 1, 1)
) as rn,
#type,
#ref
FROM t
CROSS JOIN ( SELECT #rn := 0, #type := 'a', #ref := '') as var
ORDER BY `wsf_ref`, `type`, `wsf_value` DESC;
You can see first row enter the last condition and set both variable correct:
OUTPUT
| id | wsf_ref | status | type | wsf_progress | wsf_value | rn | #type | #ref |
|----|---------|----------|--------|--------------|-----------|----|--------|------|
| 6 | 1 | Approved | blue | Day 1 | 25 | 1 | blue | 1 |
| 5 | 1 | Approved | blue | Day 1 | 10 | 2 | blue | 1 |
| 3 | 1 | Approved | orange | Day 1 | 20 | 1 | orange | 1 |
Buf if wsf_ref is a VARCHAR i got a different result
SQL DEMO #2
CREATE TABLE t (
`id` INTEGER,
`wsf_ref` VARCHAR(255),
`status` VARCHAR(255),
`type` VARCHAR(255),
`wsf_progress` VARCHAR(5),
`wsf_value` INTEGER
);
Here you can see first row the variable #type isnt set and still have a
OUTPUT
| id | wsf_ref | status | type | wsf_progress | wsf_value | rn | #type | #ref |
|----|----------|----------|--------|--------------|-----------|----|--------|----------|
| 3 | WSF19-01 | Approved | Perch | Day 2 | 20 | 1 | a | WSF19-01 |
| 4 | WSF19-01 | Approved | Perch | Day 2 | 10 | 1 | Perch | WSF19-01 |
After some debuging I found the problem is with the last assignment
if ( (#ref := `wsf_ref`) and (#type := `type`), 1, 1)
On first case when wsf_ref is integer the assignment evaluate to true and then the second condition is also checked. On the second case when wsf_ref is string the result is false and the second condition is ignored.
I change the condition to:
if ( (#ref := `wsf_ref`) OR (#type := `type`), 1, 1)
So even if the first condition is false still try to evaluate the second condition and now both query are working ok.
So why assign #ref a number get a different boolean than assign a string?
Your expression is:
if ( (#ref := `wsf_ref`) and (#type := `type`), 1, 1)
MySQL does not necessarily evaluate both conditions. It only needs to evaluate the "second" one if the "first" evaluates to true. (I put "first" and "second" in quotes because the order of evaluation is not determined, but the idea is the same regardless.)
When these are strings, the result of #ref := wsf_rf is a string. The string is converted to a boolean, via a number. The value is 0 -- which is false -- unless the string happens to start with digit.
Hence, both conditions are not evaluated and the second is not assigned.
I would write this as:
SELECT t.*,
(#rn := if(#tr = CONCAT_WS(':', wsf_ref, type),
#rn + 1,
if(#tr := CONCAT_WS(':', wsf_ref, type), 1, 1
)
)
) as rn
FROM (SELECT t.*
FROM t
ORDER BY `wsf_ref`, `type`, `wsf_value` DESC
) t CROSS JOIN
(SELECT #rn := 0, #tr := '') params;
I moved the ORDER BY to a subquery because more recent versions of MySQL don't handle ORDER BY and variables very well.
I am not an expert in MySQL and not sure how to solve this using MySQL only.
I have table like this:
mysql> describe items;
+-------------+---------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+---------------------+------+-----+---------+----------------+
| itemID | bigint(20) unsigned | NO | PRI | NULL | auto_increment |
| date | timestamp | NO | | | | |
+-------------+---------------------+------+-----+---------+----------------+
And I would like to get the position of an item as if I will make a selection and sort (or filter). For example, I sort by date and item with itemID=79 will be item with index 126 if I will make a loop. So I would like to get this number 126 directly from database. This is probably similar to ranking but I am not sure but I am not sure if it is possible.
SELECT itemID, date
FROM (
SELECT itemID, date, #rn:= #rn +1 as rank
FROM items, (SELECT #rn := 0) as para
ORDER BY date
) T
WHERE rank = 126
OR
SELECT itemID, date, rank
FROM (
SELECT itemID, date, #rn:= #rn +1 as rank
FROM items, (SELECT #rn := 0) as para
ORDER BY date
) T
WHERE itemID = 79
query
SELECT
(SELECT NAME FROM product_component) AS pcNAME,
(SELECT PROJECT_NAME FROM jira_project) AS jpNAME,
(SELECT FILTER_NAME FROM jira_filter) AS jfNAME
Each SELECT will return an indeterminate number of rows. I get the error Subquery returns more than 1 row. My desired output will be something like this (quick sketch):
=======================================
| pcNAME | jpNAME | jfNAME |
=======================================
| data | data | data |
+------------+------------+-----------+
| data | data | data |
+------------+------------+-----------+
| data | data | data |
+------------+------------+-----------+
| | data | data |
+------------+------------+-----------+
| | data | data |
+------------+------------+-----------+
| | data | |
+------------+------------+-----------+
Each column may produce a different number of rows than the others. So I will want to produce the amount of rows from the max and then blank out the others that don't fill the max number of rows.
NOTE: None of these tables have a shared column so cannot achieve as INNER JOIN
Any ideas on how this can be achieved?
One way to handle this in MySQL to use to variables, union all and aggregation:
SELECT MAX(NAME) as NAME, MAX(PROJECT_NAME) as PROJECT_NAME,
MAX(FILTER_NAME) as FILTER_NAME
FROM ((SELECT (#rnpc := #rnpc + 1) as rn, NAME, NULL as PROJECT_NAME, NULL as FILTER_NAME
FROM product_component CROSS JOIN
(SELECT #rnpc := 0) params
) UNION ALL
(SELECT (#rnpn := #rnpn + 1) as rn, NULL, PROJECT_NAME, NULL as FILTER_NAME
FROM jira_project CROSS JOIN
(SELECT #rnpn := 0) params
) UNION ALL
(SELECT (#rnf := #rnf + 1) as rn, NAME, NULL as PROJECT_NAME, NULL as FILTER_NAME
FROM jira_filter CROSS JOIN
(SELECT #rnf := 0) params
)
) t
GROUP BY rn
ORDER BY rn;
Using MySQL, I am trying to find the highest number of consecutive rows in a table based on a value. For the sake of simplicity, my table looks like this:
+----+-------+
| ID | VALUE |
+----+-------+
| 1 | A |
| 2 | B |
| 3 | A |
| 4 | A |
| 5 | B |
| 6 | B |
| 7 | A |
| 8 | A |
| 9 | A |
| 10 | B |
+----+-------+
In this example, if I wanted the highest number of consecutive rows for 'A', I would get 3. For 'B', I would get 2. Even returning a result set of the counts of consecutive rows for 'A' would be preferable. I am newer to SQL so hints would be appreciated too. Any suggestions?
You can do it using variables:
SELECT VALUE, MAX(cnt) AS maxCount
FROM (
SELECT VALUE, COUNT(grp) AS cnt
FROM (
SELECT ID, VALUE, rn - rnByVal AS grp
FROM (
SELECT ID, VALUE,
#rn := #rn + 1 AS rn,
#rnByVal := IF (#val = VALUE,
IF (#val := VALUE, #rnByVal + 1, #rnByVal + 1),
IF (#val := VALUE, 1, 1)) AS rnByVal
FROM mytable
CROSS JOIN (SELECT #rn := 0, #rnByVal := 0, #val := '') AS vars
ORDER BY ID) AS t
) AS s
GROUP BY VALUE, grp ) AS u
GROUP BY VALUE
Variables #rn and #rnByVal are used in order to simulate ROW_NUMBER window function, currently not available in MySQL. The second variable (#rnByVal) performs a count over VALUE partitions.
Using #rn - #rnByVal in an outer query we can calculate grp field, which identifies islands of consecutive rows having the same VALUE. Performing a GROUP BY on VALUE, grp we can calculate the population of these islands and, finally, in the outermost query, get the max population per VALUE.
Demo here
I'm using the following mysql query to create a pagination array -- for a list of documents -- in the form "Ab-Cf | Cg-Le | Li-Ru " etc...
The subquery 'Subquery' selects the entire list of documents, and is variable depending on the user privileges, requirements etc -- so I'm trying to avoid altering that part of the query (and have used a simplified version here).
I'm then selecting the first and last row of each page range -- i.e, the 1st and 10th row, the 11th and 20th row etc., determined by $num_rows_per_page.
SELECT * FROM
(
SELECT #row := #row + 1 AS `rownum`, `sort_field` FROM
(
SELECT #row := 0 ) r, (
SELECT D.`id`, D.`display_name` as display_field,
D.`sort_name` as sort_field
FROM Document D ORDER BY `sort_field` ASC
) Subquery
) Sorted
WHERE rownum % $num_rows_per_page = 1 OR rownum % $num_rows_per_page = 0
This is working just fine, and gives me a result set like:
+---------+-----------------------------------+
| rownum | index_field |
+---------+-----------------------------------+
| 1 | Alternaria humicola |
| 10 | Anoplophora chinensis |
| 11 | Atherigona soccata |
| 20 | Carlavirus RCVMV |
| 21 | Cephus pygmaeus |
| 30 | Colletotrichum truncatum |
| 31 | Fusarium oxysporium f. sp. ciceri |
| 40 | Homalodisca vitripennis |
| 41 | Hordevirus BSMV |
| 50 | Mayetiola hordei |
| 51 | Meromyza saltatrix |
| 60 | Phyllophaga |
| 61 | Pyrenophora teres |
+--------+------------------------------------+
However -- I can't for the life of me work out how to include the last row of the subquery in the result set. I.e., the row with rownum 67 (or whatever) that does not meet the criteria of the WHERE clause.
I was hoping to somehow pull the maximum value of rownum and add it to the WHERE clause, but I'm having no joy.
Any ideas?
Happy to try to rephrase if this isn't clear!
Edit -- here's a more appropriate version of the subquery:
SELECT * FROM
(
SELECT #row := #row + 1 AS `rownum`, `sort_field` FROM
(
SELECT #row := 0 ) r,
(
SELECT D.`id`, D.`display_name` as display_field,
D.`sort_name` as sort_field
FROM Document D INNER JOIN
(
SELECT DS.* FROM Document_Status DS INNER JOIN
(
SELECT `document_id`, max(`datetime`) as `MaxDateTime`
FROM Document_Status GROUP BY `document_id`
)
GS ON DS.`document_id` = GS.`document_id`
AND DS.`datetime` = GS.`MaxDateTime`
AND DS.`status` = 'approved' INNER JOIN
(
SELECT `id` FROM Document WHERE `template_id`= 2 ) GD
ON DS.`document_id` = GD.`id`
)
AG ON D.id = AG.document_id ORDER BY `sort_field` ASC
) Subquery
) Sorted
WHERE rownum % $num_rows_per_page = 1 OR rownum % $num_rows_per_page = 0
But, a key point to remember is that the subquery will change depending on the context.
Please try adding
OR rownum=#row
to your WHERE clause (in my testing case this works)