I am using below query
select id,
number_sequ,
startvalue
lead(startvalue,1,0) over (partition by id order by number_sequ) AS End_value
from mytable
to populate the following output
id number_sequ startvalue End_value
---- ----- ---------- -----------
AAA 1 30 20
AAA 2 20 10
AAA 4 10 15
AAA 5 15 0
BBB 1 12 23
BBB 3 23 34
BBB 4 34 0
But there are missing records in sequence
id number_sequ startvalue End_value
---- ----- ---------- -----------
AAA 3
BBB 2
I have tried different ways to find out missing numbers in Sequence and try to insert with 0 values. after that i can use lead function. unable to find out efficient way
INSERT INTO mytable (id, number_sequ, startvalue)
select id ,number_sequ ,'0'
from mytable
where (some condition to specify missing data)
Can any one help me to solve above issue.
You can get the missing values with the following approach: generate all possible values and then filter out the ones that exist.
select i.id, n.n, 0 as start_value
from (select id, min(number_seq) as min_ns, max(number_seq) as max_ns
from mytable
group by id
) i join
(select row_number() over (partition by number_seq) as n
from mytable
) n
on n.n <= i.max_ns left join -- just a bunch of numbers
mytable t
on t.id = i.id and
t.number_seq = n.n
where t.id is null;
You can pop the insert before the select to insert these values into your table.
Note that this generates the sequence numbers you need using the original data. So it assumes that your table has enough rows for the numbers you need.
If the missing values are always in between existing values, you can find the gaps using the Snowflake's JavaScript UDTFs
For example, here's a function that finds gaps in a sequence, and then we use it to generate "empty" rows:
create or replace table x(id int, seq int, startVal int) as select * from
values(1,1,11),(1,2,12),(1,4,14),(2,2,22),(2,5,25);
CREATE OR REPLACE FUNCTION find_gaps(SEQ float)
RETURNS TABLE (GAP float)
LANGUAGE JAVASCRIPT
AS '
{
initialize: function(argumentInfo, context) {
this.lastRow = null;
},
processRow: function (row, rowWriter, context) {
let curRow = row.SEQ;
if (this.lastRow == null || this.lastRow + 1 == curRow) {
this.lastRow = curRow;
} else {
while (this.lastRow + 1 < curRow) {
this.lastRow++;
rowWriter.writeRow({GAP: this.lastRow});
}
}
}
}'
;
select id, seq, startVal from x
union all
select id, gap, 0 from x,
table(find_gaps(seq::float)
over (partition by id order by seq));
----+-----+----------+
ID | SEQ | STARTVAL |
----+-----+----------+
1 | 1 | 11 |
1 | 2 | 12 |
1 | 4 | 14 |
2 | 2 | 22 |
2 | 5 | 25 |
2 | 3 | 0 |
2 | 4 | 0 |
1 | 3 | 0 |
----+-----+----------+
You can use variations of this function also e.g. if you know the range of your values per id, just feed it expected min/max as well. Also, you might need something special if your input contains NULL values (but then - what should be the result? :))
Apart from the suggested solutions, if you still want to stick to Lead function,
Lead function analyses data that has values, the result of it can have null values based on partition but the data it uses for analyses should have values. In my view, What you did is right to include the missing sequence in your result.
http://www.mysqltutorial.org/mysql-window-functions/mysql-lead-function/
Related
Edit 2021: I want to explain this better.
I have a table named MyTable with two columns; one column named SortColumn type Integer and one column named ExpressionColumn type Boolean.
I want to get all rows, sorted by SortColumn in ascending order, after the last row where ExpressionColumn was True.
The types are not exact.
Eg. Table with rows represented as [SortColumn,ExpressionColumn], [0:True] will get [0:True], [0:True, 1:False] will get [], [0:True, 1:False, 2:True, 3:True] will get [3:True, 4:True].
Leaving the old question below so as not to invalidate given answers. It had too many extra details.
I want to select rows after than last row where column Number is 0.
So with this table,
Id | Number
------------
0 | 5
1 | 30
2 | 10
3 | 25
I want to get rows with Id 0 to 3 inclusive.
With this table,
Id | Number
------------
0 | 5
1 | 30
2 | 10
3 | 25
4 | 0
I want to get no rows at all.
With this table,
Id | Number
------------
0 | 5
1 | 30
2 | 10
3 | 25
4 | 0
5 | 0
6 | 30
I want to get row with Id 6.
SQL details: MySQL 5.6.
Retrieve separate records:
SELECT *
FROM transaction t1
WHERE Name LIKE '%Car Wash%'
AND NOT EXISTS ( SELECT NULL
FROM transaction
WHERE t1.id <= id
AND Name LIKE '%Car Wash%' -- maybe not needed? not specified
AND Price = 0 );
Retrieve their amount only:
SELECT COUNT(*)
FROM transaction t1
WHERE Name LIKE '%Car Wash%'
AND NOT EXISTS ( SELECT NULL
FROM transaction
WHERE t1.id <= id
AND Name LIKE '%Car Wash%'
AND Price = 0 );
fiddle
you can select
select
from my_table
where name like '%car%' and price > 0
and for count
select count(*)
from (
select
from my_table
where name like '%car%' and price > 0
) t
I interpret this question as "how many rows are there for "car wash" after the first row with price > 0". If so:
select count(*)
from (select t.*,
min(case when price = 0 then id end) over () as id_at_0
from transaction t
) t
where name = 'Car Wash' and id > id_at_0
SELECT * FROM MyTable myTable1
AND NOT EXISTS ( SELECT NULL FROM MyTable
WHERE myTable1.SortColumn <= SortColumn
AND ExpressionColumn = True );
I have this in my database:
75012
75016
94400
94500
94300
78400
I would like to select only the string where only the first two numbers match and show how many 94 there are so it will output 75012 = 2, 94 = 3, 78 = 1.
Here is what I tried:
select cpostal from fiche_personne WHERE cpostal LIKE LEFT(cpostal, 2);
you need to use a group by clause in your query.
SELECT LEFT(cpostal,2), COUNT(*) AS total
FROM fiche_personne
GROUP BY LEFT(cpostal,2)
please note that the COUNT(*) isn't the best way to complete the query but I don't know your actual table structure, so you should change this to an actual column name
select count(cpostal) from fiche_personne WHERE cpostal LEFT(cpostal, 2) = 94;
Resource: https://www.w3schools.com/sql/func_mysql_count.asp
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(i INT NOT NULL PRIMARY KEY);
INSERT INTO my_table VALUES
(75012),
(75016),
(94400),
(94500),
(94300),
(78400);
SELECT MIN(i) i, COUNT(*) total FROM my_table GROUP BY LEFT(i,2);
+-------+-------+
| i | total |
+-------+-------+
| 75012 | 2 |
| 78400 | 1 |
| 94300 | 3 |
+-------+-------+
I am trying for a MySQL query to check multiple values between two columns values.
For example, for one value here is my query which works
SELECT column3
FROM table
WHERE (12 between minvaluecol AND maxvaluecol) AND
id = 123;
I would like to check multiple values like (12,13,14,67,68) and should return the values that are in between minvaluecol and maxvaluecol columns. In this case, only 12,13,14 are in between minvaluecol and maxvaluecol columns whereas 67,68 are not.
my table looks like this,
id | minvaluecol | maxvaluecol
---- | ----------- | ------------
121 | 23 | 35
123 | 10 | 20
125 | 40 | 50
output for id 123 should look like,
12 | true
13 | true
14 | true
67 | false
68 | false
Please help me with this query in MySQL. Thank you.
Update
Completely revamped the answer based on updated question.
As you need all the values as different rows, you need to SELECT all of them with UNION and LEFT JOIN it with the original table, e.g.:
SELECT a.val, IF(a.val BETWEEN tv.minvaluecol AND maxvaluecol, 'true', 'false') AS result
FROM (
SELECT 12 AS val
UNION
SELECT 13 AS val
UNION
SELECT 14 AS val
UNION
SELECT 67 AS val
UNION
SELECT 68 AS val) a
JOIN test_values tv
WHERE tv.id = 123;
Here's the SQL Fiddle.
The easiest way to get your result is to Insert those values into a table and then join like this:
SELECT value,
CASE WHEN value between minvaluecol AND maxvaluecol THEN 'true ELSE 'false' END
FROM table
CROSS JOIN table_with_values
WHERE id = 123
ORDER BY value
I am having problems with a specific query - respectively creating the query in the first place.
The columns can be reduced to id, seconds and status.
=============================
| id | seconds | status |
-----------------------------
| 0 | 0 | 0 |
| 1 | 12 | 1 |
| 2 | 25 | 0 |
| 3 | 37 | 1 |
| 4 | 42 | 0 |
=============================
What I'd like to have: All entries with status = 1 PLUS all entries that are less than 10 seconds away from those entries. Basically, I want to fetch all possible pairs (or triplets, etc.) of rows to check manually (later automatically) whether they need to be paired (there is a column parent_id for this purpose, but we don't need that for the query). I could do this in code (first select all status=1, then loop), but I wonder whether it is possible to do this purely in the database.
Thus, my desired output would be the following:
=============================
| id | seconds | status |
-----------------------------
| 1 | 12 | 1 | <- status = 1
| 3 | 37 | 1 | <- status = 1
| 4 | 42 | 0 | <- only 5 seconds after status = 1
=============================
My current best guess is this:
SELECT * FROM entries e0
WHERE
e0.status = 1 OR
e0.status = 0 AND
0 < (SELECT count(*)
FROM entries e1
WHERE e1.status = 1 AND abs(e1.seconds - e0.seconds) < 10)
But this fetches the whole table, and I don't really know why - and it takes a long time to do so (there is an index on the column seconds, the table has 9000 entries).
Is there a way to do this (maybe even effiently)?
Here's one option with union all and exists:
select * from entries where status = 1
union all
select * from entries e where status = 0 and
exists (select 1
from entries e2
where e2.status = 1 and
abs(e.seconds - e2.seconds) < 10
)
SQL Fiddle Demo
Alternatively you could use an outer join with distinct instead of exists:
select distinct e.*
from entries e
left join entries e2 on e2.status = 1
where e.status = 1 or abs(e.seconds - e2.seconds) < 10
More Fiddle
I prefer to do it in a single query. However there are also ways of doing it with exists or subqueries as well. Utilizing an outer join means you can grab everything at once with a nicely crafted where and join statements, adding a group by or distinct based on your performance situation will tidy up your results and make them unique rows.
My suggestion on where statements to ensure your intentions are met is to use parenthesis to establish your intended precedence. It will make your code clearer to your intentions.
WHERE Condition1 = True OR Condition2 = True AND Condition3 = True
Should be
WHERE Condition1 = True OR (Condition2 = True AND Condition3 = True)
Oddly, I would not have thought it would evaluate in the manner you mention because of past experience but then again I ALWAYS use parenthesis to establish my precedence to make it more clear and easier to craft more complex conditions.
Reason you are getting the whole table. Is because of the data in your table. Seriously, sometimes we go looking for the answer and make it complicated, I prefer my way of solving the query of yours but given your result set example my query and yours get the same results! Try changing the 10 seconds down to 1/2/3 etc and see what the effect of your query is. My assumption would be in your full dataset that your any record with a status of 0 is within 10 seconds of a record that has a status of 1...... I would have commented back but this is one of the first questions I have answered.
Here is some example code based on your dataset and query.
DECLARE #Entries AS TABLE (
Id INT
,Seconds INT
,[Status] BIT
)
INSERT INTO #Entries (Id, Seconds, [Status])
VALUES (0,0,0 )
,(1,12,1 )
,(2,25,0 )
,(3,37,1 )
,(4,42,0 )
SELECT *
FROM
#Entries e0
WHERE
e0.Status = 1
OR e0.Status = 0
AND 0 < (SELECT count(*)
FROM
#Entries e1
WHERE e1.Status = 1 AND ABS(e1.Seconds - e0.Seconds) < 10)
SELECT DISTINCT
e0.*
FROM
#Entries e0
LEFT JOIN #Entries e1
ON e1.[Status] = 1
AND ABS(e1.seconds - e0.seconds) < 10
WHERE
e0.[Status] = 1
OR e1.id IS NOT NULL
I have a table like this:
id | val
---------
1 | abc
2 | def
5 | xyz
6 | foo
8 | bar
and a query like
SELECT id, val FROM tab WHERE id IN (1,2,3,4,5)
which returns
id | val
---------
1 | abc
2 | def
5 | xyz
Is there a way to make it return NULLs on missing ids, that is
id | val
---------
1 | abc
2 | def
3 | NULL
4 | NULL
5 | xyz
I guess there should be a tricky LEFT JOIN with itself, but can't wrap my head around it.
EDIT: I see people are thinking I want to "fill the gaps" in a sequence, but actually what I want is to substitute NULL for the missing values from the IN list. For example, this
SELECT id, val FROM tab WHERE id IN (1,100,8,200)
should return
id | val
---------
1 | abc
100 | NULL
8 | bar
200 | NULL
Also, the order doesn't matter much.
EDIT2: Just adding a couple of related links:
How to select multiple rows filled with constants?
Is it possible to have a tableless select with multiple rows?
You could use this trick:
SELECT v.id, t.val
FROM
(SELECT 1 AS id
UNION ALL SELECT 2
UNION ALL SELECT 3
UNION ALL SELECT 4
UNION ALL SELECT 5) v
LEFT JOIN tab t
ON v.id = t.id
Please see fiddle here.
Yes, you can. But that will be tricky since there are no sequences in MySQL.
I assume you want just any selection, so it's:
SELECT
*
FROM
(SELECT
(two_1.id + two_2.id + two_4.id +
two_8.id + two_16.id) AS id
FROM
(SELECT 0 AS id UNION ALL SELECT 1 AS id) AS two_1
CROSS JOIN (SELECT 0 id UNION ALL SELECT 2 id) AS two_2
CROSS JOIN (SELECT 0 id UNION ALL SELECT 4 id) AS two_4
CROSS JOIN (SELECT 0 id UNION ALL SELECT 8 id) AS two_8
CROSS JOIN (SELECT 0 id UNION ALL SELECT 16 id) AS two_16
) AS sequence
LEFT JOIN
t
ON sequence.id=t.id
WHERE
sequence.id IN (1,2,3,4,5);
(check the fiddle)
It will work as combination of powers of 2 to generate consecutive table of numbers. Your values are passed to WHERE clause, so you can substitute there any set of values.
I would recommend you to use application for this case - because it will be faster. It may have some sense if you want to use this row set somewhere else (i.e. in some other queries) - but if not, it's a work for your application.
If you'll need higher values, add more rows to sequence generator, like in this fiddle.