I have a table which looks like this:
+-----------------------
| id | first_name
+-----------------------
| AC0089 | John |
| AC0015 | Dan |
| AC0017 | Marry |
| AC0003 | Andy |
| AC0001 | Trent |
| AC0006 | Smith |
+-----------------------
I need a query to split the id in the range of 3 and also display the starting id of that range i.e.
+------------+----------+--------
| startrange | endrange | id
+------------+----------+--------
| 1 | 3 | AC0089
| 4 | 6 | AC0003
+------------+----------+--------
I am pretty new to SQL and trying the below query but I dont think I am near to the correct solution at all ! Here is the query:
select startrange, endrange, id from table inner join (select 1 startRange, 3 endrange union all select 4 startRange, 6 endRange) r group by r.startRange, r.endRange;
It is giving the same id every-time and I am not able to come up with any other solution. How Can I get the required output?
Try this
SET #ct := 0;
select startrange,(startrange + 2) as endrange, seq_no from
(select (c.st - (select count(*) from <table_name>)) as startrange, c.* from
(select (#ct := #ct + 1) as st, b.* from <table_name> as b) c
having startrange mod 3 = 1) as cc;
sorry for formating.
I'm not completely sure what your trying to do but if you're trying to convert a table of ID's into ranges use a case when.
CASE WHEN startrange in(1,2,3) THEN 1
ELSE NULL
END as startrange,
CASE WHEN endrange in(1,2,3) THEN 3
ELSE NULL
END as endrange,
CASE WHEN ID in(1,2,3) THEN id
WHEN ID in(4,5,6) THEN id
ELSE id
END AS ID
Related
I have the following table structure in a mysql database.
id | files | status
1 a.pdf,b.pdf,c.pdx 1
2 d.pdf,e.pdf.g.pdf 2
3 x.pdf,k.pdf,y.pdf 1
As you can see, the attachments are all stored on a single line.
My query is supposed to select all rows where status = 1 so i'm expecting the data in the following format.
1 1 a.pdf 1
2 1 b.pdf 1
3 1 c.pdf 1
4 3 x.pdf 1
5 3 k.pdf 1
6 3 y.pdf 1
Unfortunately, I am unsure which operator I can use to accomplish this. I'm aware SQL has pivot but i doubt even that can address my issue.
As a result, I would appreciate if I could get any help in the condition of the select query.
Regards
Try this:
SET #row_num = 0;
SELECT
(#row_num := #row_num + 1) ROW_NUM,
id,
SUBSTRING_INDEX(SUBSTRING_INDEX(files, ',', idx), ',', -1) FileName,
status
FROM
test
JOIN
(SELECT 1 idx UNION ALL SELECT 2 idx UNION ALL SELECT 3 idx UNION ALL SELECT 4 idx UNION ALL SELECT 5 idx) idxs
on idxs.idx - 2 < LENGTH(files) - LENGTH(REPLACE(files, ",", ""))
WHERE status = 1
;
Just add as many indexes as your max count of files in a string.
Note that there should be no comma fater last filename. This is magic -2 is responsible for.
Consider the following...
I have a table (ints) of integers (0-9)...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,files VARCHAR(100) NOT NULL
,status INT NOT NULL
);
INSERT INTO my_table VALUES
(1,'a.pdf,b.pdf,c.pdx',1),
(2,'d.pdf,e.pdf,g.pdf',2),
(3,'x.pdf,k.pdf,y.pdf',1);
SELECT DISTINCT id
, SUBSTRING_INDEX(SUBSTRING_INDEX(files,',',i2.i*10+i1.i+1),',',-1) file
, status
FROM my_table
, ints i1
, ints i2
WHERE status = 1
ORDER
BY id, i2.i*10+i1.i;
+----+-------+--------+
| id | file | status |
+----+-------+--------+
| 1 | a.pdf | 1 |
| 1 | b.pdf | 1 |
| 1 | c.pdx | 1 |
| 3 | x.pdf | 1 |
| 3 | k.pdf | 1 |
| 3 | y.pdf | 1 |
+----+-------+--------+
I want two display the result of the second table 'e_value', wich are two records (from only one column), as two columns for the select query from first table 'e_order_item'.
Also I am displaying many order items using a parameter 'collect_id',
so I want to display each two values of the table 'e_value' using to the order item id displayed on the select query.
for example, I have this on the tables
+-------------------------------+
| e_order_item |
+-------------------------------+
| oi_id oi_price oi_collect_id |
| 1 100 2 |
| 2 30 2 |
| 3 55 3 |
| 4 70 4 |
| 5 220 2 |
| 6 300 2 |
+-------------------------------+
+----------------------------+
| e_value |
+----------------------------+
| v_id v_value v_oi_id |
| 1 name1 1 |
| 2 surname1 1 |
| 3 name2 2 |
| 4 surname2 2 |
| 5 name3 5 |
| 6 surname3 5 |
+----------------------------+
I want to select the order_items that have collect_id = 2, and I want the result to be like this
+--------------------------------------------------+
| |
+--------------------------------------------------+
| Result |
| oi_id oi_price oi_collect_id name surname |
| 1 100 2 name1 surname1 |
| 2 30 2 name2 surname2 |
| 5 220 2 name3 surname3 |
| 6 300 2 null null |
| |
+--------------------------------------------------+
Here's the query:
SELECT
t.oi_id,
t.oi_price,
t.oi_collect_id,
LEFT (
GROUP_CONCAT(t.v_value),
IF (
LOCATE(',',GROUP_CONCAT(t.v_value)) = 0,
LENGTH(GROUP_CONCAT(t.v_value)),
LOCATE(',', GROUP_CONCAT(t.v_value)) - 1
)
) 'Name',
RIGHT (
GROUP_CONCAT(t.v_value),
LENGTH(GROUP_CONCAT(t.v_value)) -
IF (
LOCATE(',',GROUP_CONCAT(t.v_value)) = 0,
LENGTH(GROUP_CONCAT(t.v_value)),
LOCATE(',',GROUP_CONCAT(t.v_value))
)
) Surname
FROM
(
SELECT
*
FROM e_order_item
LEFT JOIN e_value ON e_order_item.oi_id = e_value.v_oi_id
WHERE e_order_item.oi_collect_id = 2
ORDER BY oi_id, v_id
) t
GROUP BY t.oi_id;
DEMO HERE
Note:
The following example illustrates how we can get the first string and second string from a comma separated string.
SET #str := 'A,BCDEFGHIJKL';
SELECT
LEFT(#str,IF(LOCATE(',',#str) = 0, LENGTH(#str),LOCATE(',',#str)-1)) AS StringBeforeComma,
RIGHT(#str,LENGTH(#str)-IF(LOCATE(',',#str)=0,LENGTH(#str),LOCATE(',',#str))) AS StringAfterComma
Result:
StringBeforeComma StringAfterComma
A BCDEFGHIJKL
You have to go for pivoting to get the desired result.
select oi_id, oi_price, oi_collect_id
, max(name) as name
, max(surname) as surname
from (
select
i.oi_id, i.oi_price, i.oi_collect_id
, case when #prevVal <> (#currVal:=v.v_oi_id)
then v.v_value
else null
end as name
, case when #prevVal = #currVal
then v.v_value
else null
end as surname
, #prevVal:=#currVal as temp_currVal
from e_order_item i
left join e_value v on v.v_oi_id = i.oi_id,
(select #prevVal:=-1, #currVal:=-1) as inits
where i.oi_collect_id=2
) as main_data
group by oi_id, oi_price, oi_collect_id
order by 1;
This is tested and run successfully...and give output as you want...
There are two subqueries:
1.First will give all result having collect_id = 2...
1.SELECT tab1.oi_id, tab1.oi_price, tab1.oi_collect_id
from(
SELECT oi_id, oi_price, oi_collect_id
from e_order_item
where oi_collect_id = 2
) as tab1;
2.This query will give you name, surname and id in different columns..
2.(SELECT e.v_value as name, surname, id
from (
select t1.v_value as surname, t1.v_oi_id as id from e_value as t1
group by t1.v_oi_id
)join e_value as e on id = e.v_oi_id and surname <> e.v_value
) as tab2 on tab1.oi_id = tab2.id;
Now left join these two query to get our desired result as:
SELECT tab1.oi_id, tab1.oi_price, tab1.oi_collect_id, name, surname
from(
SELECT oi_id, oi_price, oi_collect_id
from e_order_item
where oi_collect_id = 2
) as tab1 left join
(SELECT e.v_value as name, surname, id
from (
select t1.v_value as surname, t1.v_oi_id as id from e_value as t1
group by t1.v_oi_id
)join e_value as e on id = e.v_oi_id and surname <> e.v_value
) as tab2 on tab1.oi_id = tab2.id
order by tab1.oi_id asc; // to print in ascending order..
Why we use left join..You can use this link http://www.w3schools.com/sql/sql_join_left.asp to understand properly...
If this solution is helpful then let me know...
The subject of the question is not very explanatory, sorry for that.
Ya so the question follows:
I have a database structure as below where pk is primary key, id
is something which is multiple for many rows.
+------+------+---------------------+
| pk | id | value |
+------+------+---------------------+
| 99 | 1 | 2013-08-06 11:10:00 |
| 100 | 1 | 2013-08-06 11:15:00 |
| 101 | 1 | 2013-08-06 11:20:00 |
| 102 | 1 | 2013-08-06 11:25:00 |
| 103 | 2 | 2013-08-06 15:10:00 |
| 104 | 2 | 2013-08-06 15:15:00 |
| 105 | 2 | 2013-08-06 15:20:00 |
+------+------+---------------------+
What is really need to get is, value difference between first two rows (which is ordered by value) for each
group (where group is by id). So according to above structure I need
timediff(value100, value99) [ which is for id 1 group]
and timediff(value104, value103) [ which is for id 2 group]
i.e. value difference of time ordered by value for 1st two rows in each group.
One way i can think to do is by 3 self joins (or 3 sub queries) so as to find the
first two in 2 of them , and third query subtracting it. Any suggestions?
try this.. CTE is pretty powerfull!
WITH CTE AS (
SELECT
value, pk, id,
rnk = ROW_NUMBER() OVER ( PARTITION BY id order by id DESC)
, rownum = ROW_NUMBER() OVER (ORDER BY id, pk)
FROM test
)
SELECT
curr.rnk, prev.rnk, curr.rownum, prev.rownum, curr.pk, prev.pk, curr.id, prev.id, curr.value, prev.value, curr.value - prev.value
FROM CTE curr
INNER JOIN CTE prev on curr.rownum = prev.rownum -1 and curr.id = prev.id
and curr.rnk <=1
Looks a bit wierd... But you can try this way
SET #previous = 0;
SET #temp = 0;
SET #tempID = 0;
Above step may not be needed .. But just to make sure nothing goes wrong
SELECT pkid, id, diff, valtemp FROM (
SELECT IF(#previousID = id, #temp := #temp + 1, #temp := 1) occ, #previousID := id,
TIMEDIFF(`value`, #previous) diff, pk, id, `value`, #previous := `value`
FROM testtable) a WHERE occ = 2
Demo on sql fiddle
I have some data in database:
id user
1 zhangsan
2 zhangsan
3 zhangsan
4 lisi
5 lisi
6 lisi
7 zhangsan
8 zhangsan
I want keep order, and combine near same user items, how to do it?
When I use shell script, I will(data in file test.):
cat test|cut -d " " -f2|uniq -c
this will get result as:
3 zhangsan
3 lisi
2 zhangsan
But how to do it use sql?
If you try:
SET #name:='',#num:=0;
SELECT id,
#num:= if(#name = user, #num, #num + 1) as number,
#name := user as user
FROM foo
ORDER BY id ASC;
This gives:
+------+--------+------+
| id | number | user |
+------+--------+------+
| 1 | 1 | a |
| 2 | 1 | a |
| 3 | 1 | a |
| 4 | 2 | b |
| 5 | 2 | b |
| 6 | 2 | b |
| 7 | 3 | a |
| 8 | 3 | a |
+------+--------+------+
So then you can try:
SET #name:='',#num:=0;
SELECT COUNT(*) as count, user
FROM (
SELECT #num:= if(#name = user, #num, #num + 1) as number,
#name := user as user
FROM foo
ORDER BY id ASC
) x
GROUP BY number;
Which gives
+-------+------+
| count | user |
+-------+------+
| 3 | a |
| 3 | b |
| 2 | a |
+-------+------+
(I called my table foo and also just used names a and b because I was too lazy to write zhangsan and lisi over and over).
if in oracle, you can do like below.
SELECT NAME,
num - lagnum
FROM (SELECT lagname,
NAME,
num,
nvl(lag(num) over(ORDER BY num), 0) lagnum
FROM (SELECT id,
lag(NAME) over(ORDER BY ID) lagname,
NAME,
lead(NAME) over(ORDER BY ID) leadname,
ROWNUM num
FROM (SELECT * FROM test ORDER BY ID))
WHERE (lagname = NAME AND (NAME <> leadname OR leadname IS NULL))
OR (lagname IS NULL AND NAME <> leadname)
OR (lagname <> NAME AND leadname IS NULL)
ORDER BY ID);
if in sql server, oracle, db2...
with x as(
select c.*, rn = row_number() over (order by c.id)
from test c
left join test n
on c.[user] = n.[user]
and c.[id] + 1 = n.[id]
where n.id is null
)
select a.[user], a.id - coalesce(b.id, 0)
from x a
left join x b
on a.rn = b.rn + 1
I think what you are looking for is to COUNT(ID):
SELECT COUNT(ID) FROM table GROUP BY user
You cannot do this in sql without doing some sort of sequential (iterative) analysis. Remember sql is set operation language.
A little improvement to the selected answer would be not to have to define those variables. So this query can be solved in just a single statement:
SELECT COUNT(*) cnt, user
FROM (
SELECT #num := #num + (#name != user) as number,
#name := user as user
FROM t, (select #num := 0, #name := '') as s
ORDER BY id
) x
GROUP BY number
Output:
| CNT | USER |
|-----|----------|
| 3 | zhangsan |
| 3 | lisi |
| 2 | zhangsan |
Fiddle here
I have a (MySQL) table containing dates of the last scan of hosts combined with a report ID:
+--------------+---------------------+--------+
| host | last_scan | report |
+--------------+---------------------+--------+
| 112.86.115.0 | 2012-01-03 01:39:30 | 4 |
| 112.86.115.1 | 2012-01-03 01:39:30 | 4 |
| 112.86.115.2 | 2012-01-03 02:03:40 | 4 |
| 112.86.115.2 | 2012-01-03 04:33:47 | 5 |
| 112.86.115.1 | 2012-01-03 04:20:23 | 5 |
| 112.86.115.6 | 2012-01-03 04:20:23 | 5 |
| 112.86.115.2 | 2012-01-05 04:29:46 | 8 |
| 112.86.115.6 | 2012-01-05 04:17:35 | 8 |
| 112.86.115.5 | 2012-01-05 04:29:48 | 8 |
| 112.86.115.4 | 2012-01-05 04:17:37 | 8 |
+--------------+---------------------+--------+
I want to select a list of all hosts with the date of the last scan and the corresponding report id. I have built the following nested query, but I am sure it can be done in a single query:
SELECT rh.host, rh.report, rh.last_scan
FROM report_hosts rh
WHERE rh.report = (
SELECT rh2.report
FROM report_hosts rh2
WHERE rh2.host = rh.host
ORDER BY rh2.last_scan DESC
LIMIT 1
)
GROUP BY rh.host
Is it possible to do this with a single, non-nested query?
No, but you can do a JOIN in your query
SELECT x.*
FROM report_hosts x
INNER JOIN (
SELECT host,MAX(last_scan) AS last_scan FROM report_hosts GROUP BY host
) y ON x.host=y.host AND x.last_scan=y.last_scan
Your query is doing a filesort, which is very inefficient. My solutions doesn't. It's very advisable to create an index on this table
ALTER TABLE `report_hosts` ADD INDEX ( `host` , `last_scan` ) ;
Else your query will do a filesort twice.
If you want to select from the report_hosts table only once then you could use a sort of 'RANK OVER PARTITION' method (available in Oracle but not, sadly, in MySQL). Something like this should work:
select h.host,h.last_scan as most_recent_scan,h.report
from
(
select rh.*,
case when #curHost != rh.host then #rank := 1 else #rank := #rank+1 end as rank,
case when #curHost != rh.host then #curHost := rh.host end
from report_hosts rh
cross join (select #rank := null,#curHost = null) t
order by host asc,last_scan desc
) h
where h.rank = 1;
Granted it is still nested but it does avoid the 'double select' problem. Not sure if it will be more efficient or not - kinda depends what indexes you have and volume of data.