Say I have a table 'alphabet'. This is just a basic representation/example.
id word
1 a
2 b
3 c
4 d
5 e
6 f
7 g
8 h
9 i
10 j
11 k
12 l
13 m
Now assume I am restricted to just a single query (with subqueries) due to a language restriction or otherwise.
I want my 'result' to be as follows:
row col1 col2 col3
1 a b c
2 d e f
3 g h i
4 j k l
5 m
Now I've gotten somewhat close to this by emulating a Full Outer Join in MySQL by following the instructions found here: Full Outer Join in MySQL combined with a sub-query on the same table using something along the lines of:
SELECT id,word FROM table WHERE MOD(id,3)=1
This isn't particularly perfect, as it requires me to assume that the ids follow each-other perfectly sequentially, but I haven't been able to think of a better method at the time. Since last I recall, LIMIT and OFFSET do not take sub-queries.
However, following this thought through, results into something along the lines of:
row col1 col2 col3
1 a
2 d
3 g
4 j
5 m
6 b
7 e
8 h
9 k
10 c
11 f
12 i
13 l
13 m
Is there a way to get my desired format?
And note that normally, the desired way to do this is indeed to just do three calls with a limit-offset call based on a count(). But /is this possible/ to be done in a single call?
I doesn't found any use case for this, but it is what you want:
SELECT
FLOOR((id - 1)/3) + 1 id,
MAX(CASE WHEN MOD(id - 1,3) = 0 THEN word END) col1,
MAX(CASE WHEN MOD(id - 1,3) = 1 THEN word END) col2,
MAX(CASE WHEN MOD(id - 1,3) = 2 THEN word END) col3
FROM tbl
GROUP BY FLOOR((id - 1)/3)
SQLFIDDLE DEMO
Notice, that this will work only in case when you have sequential Id starting from 1.
Is this what you need?
SELECT FLOOR((col1.id - 1) / 3 + 1) AS id, col1.word AS col1, col2.word AS col2, col3.word AS col3
FROM alphabet col1
LEFT JOIN alphabet col2 ON col1.id = col2.id - 1
LEFT JOIN alphabet col3 ON col2.id = col3.id - 1
WHERE col1.id % 3 = 1;
How about something like
Select t1.id as `row`, t1.word as col1, t2.word as col2, t3.word as col3
From alphabet t1
left join alphabet t2 on t2.id = t1.id + 5
left join alphabet t3 on t3.id = t1.id + 10
Where t1.id <= 5
Taking Halmet Hakobyan's answer, finishing this off:
SELECT
FLOOR((rank - 1)/3) + 1 rank,
MAX(CASE WHEN MOD(rank - 1,3) = 0 THEN word END) col1,
MAX(CASE WHEN MOD(rank - 1,3) = 1 THEN word END) col2,
MAX(CASE WHEN MOD(rank - 1,3) = 2 THEN word END) col3
FROM (SELECT #rn:=#rn+1 AS rank, `id`,`word` from tbl) as tbl, (SELECT #rn:=0) t2
GROUP BY FLOOR((rank - 1)/3)
SQLFIDDLE DEMO
This will work even if the ids are not in sequence.
Related
I have a table like this in MySQL :
Group Seqno Event
1 1 A
1 2 B
1 3 C
1 4 B
1 5 E
1 6 B
1 7 D
1 8 A
I want to count all the rows from last (most recent entry) for each Group with Event = B, and return all remaining rows as soon as it hit count of 2.
The output will be
Group Seqno Event
1 4 B
1 5 E
1 6 B
1 7 D
1 8 A
Any idea how to achieve it.
You seem to want all rows from the second to last "B"?
If so, you can use a correlated subquery:
select t.*
from t
where t.seqno >= (select t2.seqno
from t t2
where t2.group = t.group and t2.event = 'B'
order by t2.seqnum desc
limit 1, 1
);
To handle the case where there may be no "second" sequence number, you can use coalesce():
select t.*
from t
where t.seqno >= coalesce( (select t2.seqno
from t t2
where t2.group = t.group and t2.event = 'B'
order by t2.seqnum desc
limit 1, 1
), t.seqno
);
I have the following data:
id userid name group
1 1 A x
2 1 A y
3 1 A z
4 2 B x
5 2 B y
6 3 C y
7 4 D x
8 5 E x
9 5 E z
10 6 F x
I want to find those records that meet all this condition:
Select all rows where the a userid belongs to a group other than y but the userid also belongs to group y.
The resulting dataset will be as follows:
id userid name group
1 1 A x
3 1 A z
4 2 B x
If you see, it has resulted in two records for userid a because these are two two records belong to groups other than y but the userid 1 also belongs to group y. Same for userid 2.
I have been breaking my head on how to get this in an SQL statement but not even close to a solution.
Any help is appreciated.
Use a join:
SELECT t1.*
FROM mytable t1
INNER JOIN mytable t2
ON t1.user_id = t2.user_id AND t1.group <> t2.group AND t2.group = 'y'
I think that would be the fastest query (but please feel free to try the other solutions as well).
Add an index on user_id if not already there and maybe play with some other indexes as well (maybe a composite index on group and user_id can be utilized)
Use exists
select *
from MyTable a2
where name_group <> 'y'
and exists (select 1
from MyTable a2
where a2.name_group = 'y'
and a2.userid = a1.userid)
You can get all the users that meet the condition using aggregation and having:
select userid
from t
group by userid
having sum( group = 'y' ) > 0 and
sum( group <> 'y') > 0;
I leave it to your to put this into a query to get all the original rows.
So I want to select rows from table where col1 or col2 equals to variable, but if there is already row selected where col1 equals to variable (variable X) and col2 is anything else (variable Y) then it won't select another row where col2 equals to variable X and col1 equals to that variable Y. Everything ordered by column TIME descending.
Let's say this is my table:
COL1 COL2 TIME COL4
1 2 0 A
1 2 1 B
2 1 2 C
1 3 3 D
3 1 4 E
4 2 5 F
3 4 6 G
1 2 7 H
4 1 8 I
And let's say that variable X equals to 1, then I want to have these rows:
COL1 COL2 TIME COL4
4 1 8 I
1 2 7 H
3 1 4 E
So it won't show me this row
COL1 COL2 TIME COL4
2 1 2 C
because there is already a combination where col1/col2 is 2/1 or 1/2.
Sorry if I explained it in a bad way, but I can't think of better explanation.
Thank you guys.
Making a couple of key assumptions...
SELECT a.*
FROM my_table a
JOIN
( SELECT MAX(time) time
FROM my_table
WHERE 1 IN (COL1,COL2)
GROUP
BY LEAST(col1,col2)
, GREATEST(col1,col2)
) b
ON b.time = a.time;
EDIT: I posted this answer when it was thought that OP's database was SQL Server. But as it turns out, the database is MySQL.
I think this query should do it:
select t.col1, t.col2, t.time, t.col4
from (select t.*,
row_number() over (
partition by
case when col1 < col2 then col1 else col2 end,
case when col1 < col2 then col2 else col1 end
order by time desc) as rn
from tbl t
where t.col1 = x or t.col2 = x) t
where t.rn = 1
order by t.time desc
The key part is defining the row_number partition by clause in such a way that (1, 2) is considered equivalent to (2, 1), which is what the case statements do. Once the partitioning works correctly, you just need to keep the first row of every "partition" (where t.rn = 1) to exclude duplicate rows.
I have a table 1 with a one to many relationship to table 2.
Table 1 also has a one to many relationship with table 3
I want to combine the results of the join but all im getting is repeated values
Here is the structure:
table 1
reportnumber
1
2
3
table 2
reportnumber col1
1 a
1 b
2 c
3 a
table 3
reportnumber col2
1 x
1 y
1 z
2 w
expected result set
reportnumber col1 col2
1 a x
1 b y
1 z
2 c w
3 a
I'm sure this is possible with a left outer join but i just cant get the syntax right
Any clues?
This is what im trying
select * from table1 a
left outer join table2 b on a.reportnumber=b.reportnumber
left outer join table3 on a.reportnumer=c.reportnumber
But the results look like this
reportnumber col1 col2
1 a x
1 a y
1 a z
1 b x
1 b y
1 b z
...
This isn't easy in MySQL, but you can do it with variables. This has little to do with a join. Or, it has a lot to do with join, but you don't have the right join keys and you don't have full outer join.
The solution is to enumerate the rows from each table with the data columns. Then aggregate using the enumeration and reportnumber:
select reportnumber, max(col1) as col1, max(col2) as col2
from ((select t2.reportnumber, col1, null as col2, #rn2 := #rn2 + 1 as rn
from table2 t2 cross join
(select #rn2 := 0) const
) union all
(select t3.reportnumber, null, t3.col2, #rn3 := #rn3 + 1 as rn
from table3 t3 cross join
(select #rn3 := 0) const
)
) t
group by reportnumber, rn;
i have two tables as follows------
table-1
CalenderType periodNumber periodstartdate
1 1 01-01-2013
1 2 11-01-2013
1 3 15-01-2013
1 4 25-01-2013
2 1 01-01-2013
2 2 15-01-2013
2 3 20-01-2013
2 4 25-01-2013
table2
Incidents Date
xyz 02-01-2013
xxyyzz 03-01-2013
ccvvb 12-01-2013
vvfg 16-01-2013
x3 17-01-2013
x5 24-01-2013
Now i want to find out the number of incidents took place in a given period(the Calendar type may change on runtime like)
the query should look something like this
select .......
from ......
where CalendarType=1
which should return
CalendarType PeriodNumber Incidents
1 1 2
1 2 1
1 3 3
1 4 0
can someone suggest me an approach or any method how this can be achieved.
Note:each period is variable in size.peroid1 may have 10 days period2 may have 5 days etc.
I think this does what you want, although I don't understand how you arrived at your sample output:
select t.CalenderType, t.periodNumber, count(*) as Incidents
from Table1 t
inner join (
select t2.Date, t2.Incidents, max(t1.periodstartdate) as PeriodStartDate
from Table2 t2
inner join Table1 t1 on t2.Date >= t1.periodstartdate
where CalenderType = 1
group by t2.Date, t2.Incidents
) a on t.periodstartdate = a.PeriodStartDate
where CalenderType=1
group by t.CalenderType, t.periodNumber
SQL Fiddle Example
Try this, a bit more general solution,SQLFiddle (Thanks RedFilter for schema):
SELECT t1.CalenderType, t1.periodNumber, count(Incidents)
FROM Table1 t1, Table1 t11, Table2
WHERE
(
(
t1.CalenderType = t11.CalenderType
AND t1.periodNumber = t11.periodNumber - 1
AND Date BETWEEN t1.periodstartdate AND t11.periodstartdate
)
OR
(
t1.periodNumber = (SELECT MAX(periodNumber) FROM Table1 WHERE t1.CalenderType = CalenderType)
AND Date > t1.periodstartdate
)
)
GROUP BY t1.CalenderType, t1.periodNumber
ORDER BY t1.CalenderType, t1.periodNumber