I have the following problem at work. I have a large table with different columns and few 100 000s of rows. I'll only post the ones im interested in.
Assume the following data set
Device ID, Feature Id, Feature Status
1, 1, 0
1, 2, 0
1, 3, 1
1, 4, 1
1, 5, 1
2, 1, 1
2, 2, 0
2, 3, 0
2, 4, 1
2, 5, 0
3, 1, 1
3, 2, 1
3, 3, 1
3, 4, 1
3, 5, 1
4, 1, 0
4, 2, 0
4, 3, 1
4, 4, 0
4, 5, 0
I need to select rows with Feature Status = 1 but only the first 2 from each Device Id.
The results of the query should be:
1,3,1
1,4,1
2,1,1
2,4,1
3,1,1
3,2,1
4,3,1
I tried something like this:
SELECT brdsurfid,featureidx,FeatStatus FROM Features F1 WHERE FeatStatus = 1 AND
(SELECT COUNT(*) FROM Features F2
WHERE F2.FeatureIdx <= F1.FeatureIdx AND F2.FeatStatus = 1) < 2
ORDER BY BrdSurfId,FeatureIdx;
which I found in another response but it didnt quite work.
I know I need to use a mix of LIMIT or COunt(*) and some nested selects but I can't figure it out. Thanks
This probably not a very efficient way to do this, but I don't think there is a better solution for sqlite (that involves a single query):
SELECT *
FROM t t0
WHERE FeatureStatus AND
(SELECT count(*)
FROM t t1
WHERE t0.DeviceID=t1.DeviceID
AND FeatureStatus
AND t1.FeatureId<t0.FeatureId
)<2;
I assume that the table is called t. The idea is to find all features where the status is 1 and then for each feature to count the previous features with that status for the same product. If that count is more than 2, then reject the row.
Not sure if this will work with sqlite but for what its worth...
;with result as
(
SELECT
brdsurfid,
featureidx,
FeatStatus ,
ROW_NUMBER() OVER(PARTITION BY brdsurfid ORDER BY fieldWhatever) AS 'someName1',
ROW_NUMBER() OVER(PARTITION BY featureidx ORDER BY fieldWhatever) AS 'someName2',
ROW_NUMBER() OVER(PARTITION BY FeatStatus ORDER BY fieldWhatever) AS 'someName3'
FROM
Features
)
SELECT *
FROM
result
WHERE
FeatStatus = 1 AND
someName1 <= 2 AND
someName2 <= 2 AND
someName3 <= 2
Related
I have a table called statuses shown below. Every time a user logs in a row is added. A status is added. I need to write a query that gives me all IDs one time that have never been 1, 6 or 8.
id
status
1
1
1
6
1
8
1
1
2
1
2
6
2
8
2
0
3
0
3
0
3
0
3
0
I wrote a query:
SELECT DISTINCT(id) FROM statuses WHERE status NOT IN (1, 6, 8)
Unfortunately this gives me ID 2 and 3, but I don't want ID 2 to be selected because they have statuses 1, 6, and 8.
Thanks for your help!
You can group by id and set the condition in the HAVING clause:
SELECT id
FROM statuses
GROUP BY id
HAVING SUM(status IN (1, 6, 8)) = 0
Or:
SELECT DISTINCT id
FROM statuses
WHERE id NOT IN (SELECT id FROM statuses WHERE status IN (1, 6, 8))
See the demo.
I'm trying to figure out counting the consecutive values row after row.
I have a mySQL / php website.
Based on Racing league results table, I have such example data:
(race id, season number, track number, first (p1), second (p2).... last (p2) filled with pilot ID).
id, season, track, p1, p2.... p20
1, 1, 1, 1, 4, .... 15
2, 1, 2, 3, 5, .... 15
3, 1, 3, 1, 4, .... 15
4, 1, 4, 1, 2, .... 15
5, 1, 5, 1, 4, .... 15
6, 1, 6, 2, 2, .... 15
7, 1, 7, 2, 4, .... 15
8, 1, 8, 1, 2, .... 15
As you can see, pilotID n.1 won 3 times consecutively, from race 3 to race 5, while pilotID n.2 won 2 times consecutively, from race 6 to race 7.
My goal output table would be:
count, pilot, fromRaceID, toRaceID
3, 1, 3, 5
2, 2, 6, 7
ordered by count.
I know it is complicated, and even mightn't be possible; I found major difficulties trying to do so, therefore your help would be extremely appreciated.
This is a type of gaps-and-islands. Subtract a sequence number and the "adjacent" values will be constant:
select p1, min(trackid), max(trackid), count(*)
from (select t.*,
row_number() over (partition by p1 order by track_number) as seqnum
from t
) t
group by p1, (track_number - seqnum)
having count(*) > 1;
EDIT:
In older versions of MySQL, you can use:
select p1, min(trackid), max(trackid), count(*)
from (select t.*,
(select count(*)
from t t2
where t2.p1 = t.p1 and t2.track_number <= t.track_number
) as seqnum
from t
) t
group by p1, (track_number - seqnum)
having count(*) > 1;
I have a column that changes values.
I want to count by adding at each change up and subtracting at each change down. Assuming x[] are my values, Delta is the sign of change in x's elements, and y[] is my targeted results or counts.
We count up until the next delta -1 at which we start counting down, then we resume counting up when delta changes back to +1. In summary we add normally until we have a delta of -1 at that time we start subtracting, then resume adding up at the next +1 delta.
x: 1, 3, 4, 4, 4, 5, 5, 3, 3, 4, 5, 5, 6, 5, 4, 4, 4, 3, 4, 5, 6, 7, 8
Delta: 0, 1, 1, 0, 0, 1, 0, -1, 0, 1, 1, 0, 1, -1, -1, 0, 0, -1, 1, 1, 1, 1, 1
y: 1, 2, 3, 4, 5, 6, 7, 6, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 5, 6, 7, 8, 9
The length of my array is in the millions of rows, and efficiency is important. Not sure if such operation should be done in SQL or whether I would be better off retrieving the data from the database and performing such calculation outside.
You could use this query in SQL-Server, presuming a PK-column for the ordering:
WITH CTE AS
(
SELECT t.ID, t.Value,
LastValue = Prev.Value,
Delta = CASE WHEN Prev.Value IS NULL
OR t.Value > Prev.Value THEN 1
WHEN t.Value = Prev.Value THEN 0
WHEN t.Value < Prev.Value THEN -1 END
FROM dbo.TableName t
OUTER APPLY (SELECT TOP 1 t2.ID, t2.Value
FROM dbo.TableName t2
WHERE t2.ID < t.ID
ORDER BY t2.ID DESC) Prev
)
, Changes AS
(
SELECT CTE.ID, CTE.Value, CTE.LastValue, CTE.Delta,
Change = CASE WHEN CTE.Delta <> 0 THEN CTE.Delta
ELSE (SELECT TOP 1 CTE2.Delta
FROM CTE CTE2
WHERE CTE2.ID < CTE.ID
AND CTE2.Delta <> 0
ORDER BY CTE2.ID DESC) END
FROM CTE
)
SELECT SUM(Change) FROM Changes c
The result is 9 as expected:
complete result set
only Sum
The OUTER APPLY links the current with the previous record, the previous record is the one with the highest ID < current.ID. It works similar to a LEFT OUTER JOIN.
The main challenge was the sub-query in the last CTE. That is necessary to find the last delta that is <> 0 to determine if the current delta is positive or negative.
You can also use LAG and SUM with OVER (Assuming you have SQL Server 2012 or above) like this.
Sample Data
DECLARE #Table1 TABLE (ID int identity(1,1), [x] int);
INSERT INTO #Table1([x])
VALUES (1),(3),(4),(4),(4),(5),(5),(3),(3),(4),(5),(5),(6),(5),(4),(4),(4),(3),(4),(5),(6),(7),(8);
Query
;WITH T1 as
(
SELECT ID,x,ISNULL(LAG(x) OVER(ORDER BY ID ASC),x - 1) as PrevVal
FROM #Table1
), T2 as
(
SELECT ID,x,PrevVal,CASE WHEN x > PrevVal THEN 1 WHEN x < PrevVal THEN -1 ELSE 0 END as delta
FROM T1
)
SELECT ID,x,SUM(COALESCE(NULLIF(T2.delta,0),TI.delta,0))OVER(ORDER BY ID) as Ordered
FROM T2 OUTER APPLY (SELECT TOP 1 delta from T2 TI WHERE TI.ID < T2.ID AND TI.x = T2.x AND TI.delta <> 0 ORDER BY ID DESC) as TI
ORDER BY ID
Output
ID x Ordered
1 1 1
2 3 2
3 4 3
4 4 4
5 4 5
6 5 6
7 5 7
8 3 6
9 3 5
10 4 6
11 5 7
12 5 8
13 6 9
14 5 8
15 4 7
16 4 6
17 4 5
18 3 4
19 4 5
20 5 6
21 6 7
22 7 8
23 8 9
You use sql-server and mysql tag. If this can be done within SQL-Server you should have a look on the OVER-clause: https://msdn.microsoft.com/en-us/library/ms189461.aspx
Assuming there's an ordering criteria it is possible to state a ROW-clause and use the value of a preceeding row. Many SQL-functions allow the usage of OVER.
You could define a computed column which does the calculation on insert...
Good luck!
Let's supouse that I have 1 table with PRODUCTS. And each product are lated to a "gender" (Female (1) or Male (2))
That Products have a boolean attribute that indicate if that product is or isn't in OFFER.
Explain example:
ID_PRODUCT, GENDER, OFFER
1, 2 (male), 0
2, 2 (male), 1
3, 2 (male), 1
4, 1 (female), 1
5, 1 (female), 1
6, 2 (male), 1
I need to get all the Offers, but "intercalated" (I don't know how say it on English... hehe)... One male, one female, one male, one female, one male... And ONLY THE OFFERS.
In this example, the response show be:
2, 2(m), 1
4, 1(f), 1
3, 2(m), 1
5, 1(f), 1
6, 2(m), 1
SELECT * FROM products WHERE offer = 1 ORDER BY ¿what?
Can I do that?
Thanks a lot in advance!
Try:
SELECT p.*,
IF(`GENDER`='2 (male)',#m:=#m+1,#f:=#f+1) As position
FROM Products p,
(SELECT #m:=0,#f:=0) x
WHERE `OFFER` = 1
ORDER BY position, `GENDER` DESC
Demo: http://sqlfiddle.com/#!2/6671ea/4
I'm sure this is possible I don't know where to start.
I have a table with 2000 values they are on the range from 0 to 100.
I want to query the table to get the different groups of values.
i.e. I have those values 5, 10 , 5 , 2 , 2, 0, 1, 1, 1, 1, 1, 10, 2
I want an output like this:
Value - Number_of_times
0 1
1 5
2 3
5 2
10 2
You need to group your results by your field, and take the COUNT() of each group:
SELECT myfield, COUNT(*) as number_of_times
FROM mytable
GROUP BY myfield