I have a table order in which there are 3 columns
BookCode Quantity Billno
--------------------------
Ed001 2 A1
Ed002 3 A1
Ed003 1 A1
py001 5 A2
ed001 1 A2
ed005 2 A2
ed003 4 A2
Now I want to show my result to user as
table Result
BookCode A1 A2
----------------------
Ed001 2 1
Ed002 3 0
Ed003 1 4
py001 0 5
ed005 0 1
Result is my temporary table, I alter it and add column according to Billno
this works fine
Now I select BookCode and Quantity from table Order and insert it into result
What I want that if there is already a bookcode exist then just update result table otherwise insert a new row with that bookcode.
What should I do for that?
I solved it using Cursor..
create table #Result(BookCode varchar(20),BookRate float)
create table #bill(bilno varchar(max) )
insert into #bill ('A1')
insert into #bill ('A2')
SET #Ordnum = CURSOR FOR
SELECT bilno FROM #bill
OPEN #Ordnum
FETCH NEXT
FROM #Ordnum INTO #billno
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC ('ALTER TABLE #temp ADD [' + #billno+ '] varchar(30)')
SET #getbookcode = CURSOR FOR
SELECT BookCode , BookQuantity , BookRate FROM OrderDetail where BillNo = #billno
OPEN #getbookcode
FETCH NEXT
FROM #getbookcode INTO #Tbookcode,#TbookQty,#TbookRate
WHILE ##FETCH_STATUS = 0
BEGIN
if exists(select BookCode from #Result where BookCode = #Tbookcode and BookRate = #TbookRate)
begin
EXEC('Update #Result set [' + #billno+']= '+#TbookQty+' where BookCode = '''+#Tbookcode+''' and BookRate = '+#TbookRate+'')
end
else
begin
EXEC('Insert Into #Result ([' + #bno + '],BookCode,BookRate) values ( '+#TbookQty +' , '''+#Tbookcode+''','+#TbookRate+')')
end
FETCH NEXT FROM #getbookcode INTO #Tbookcode,#TbookQty , #TbookRate
END
CLOSE #getbookcode
DEALLOCATE #getbookcode
FETCH NEXT FROM #Ordnum INTO #bno
END
CLOSE #Ordnum
DEALLOCATE #Ordnum
select * from #Result
Here #bill is a Temporary table in which Billnum are stored.
Use the MERGE statement. This does an INSERT or UPDATE based on the matching
Something like this, which includes the PIVOT needed
MERGE INTO result R
USING (
SELECT
ISNULL(O1.BookCode, O2.BookCode) AS BookCode,
ISNULL(O1.Quantity, 0) AS A1,
ISNULL(O2.Quantity, 0) AS A2
FROM
(SELECT * FROM #ORDER WHERE BillNo = 'A1') O1
FULL OUTER JOIN
(SELECT * FROM #ORDER WHERE BillNo = 'A2') O2
ON O1.BookCode = O2.BookCode
) src ON R.BookCode = src.BookCode
WHEN MATCHED THEN
UPDATE
SET A1 = src.A1, A2 = src.A2
WHEN NOT MATCHED BY TARGET THEN
INSERT (BookCode, A1, A2)
VALUES (src.BookCode, src.A1, src.A2);
Related
i have a table (Catalogs) on my Mariadb 10.1
id value
1 one ; two ; one
2 two ; three ; one
3 four ; five
4 one
5 four ; one
how do i count and group the value on Catalogs table like on the table below.
result count
one 5
two 2
three 1
four 2
five 1
or this table
id value
1 one
1 two
1 one
2 two
2 three
2 one
3 four
3 five
4 one
5 four
5 one
Taking reference from link http://www.marcogoncalves.com/2011/03/mysql-split-column-string-into-rows/
Assuming you have table named as table1 which contains two columns id and value and value column contains comma separated values.
Modified procedure:
CREATE PROCEDURE `explode_table`(bound VARCHAR(255))
BEGIN
DECLARE id INT DEFAULT 0;
DECLARE value TEXT;
DECLARE occurance INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE splitted_value varchar(25);
DECLARE done INT DEFAULT 0;
DECLARE cur1 CURSOR FOR SELECT table1.id, table1.value
FROM table1
WHERE table1.value != '';
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE IF EXISTS table2;
CREATE TEMPORARY TABLE table2(
`id` INT NOT NULL,
`value` VARCHAR(56) NOT NULL
) engine=memory;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO id, value;
IF done THEN
LEAVE read_loop;
END IF;
SET occurance = (SELECT LENGTH(value)
- LENGTH(REPLACE(value, bound, ''))
+1);
SET i=1;
WHILE i <= occurance DO
SET splitted_value =
trim((SELECT REPLACE(SUBSTRING(SUBSTRING_INDEX(value, bound, i),
LENGTH(SUBSTRING_INDEX(value, bound, i - 1)) + 1), ';', '')));
INSERT INTO table2 VALUES (id, splitted_value);
SET i = i + 1;
END WHILE;
END LOOP;
CLOSE cur1;
SELECT * FROM table2;
END
A plain SQL way of doing it, which will cope with up to 100 split delimited values (easily expanded to cope with more if necessary):-
SELECT result, COUNT(id)
FROM
(
SELECT id, SUBSTRING_INDEX(SUBSTRING_INDEX(value, ' ; ', tens.anum * 10 + units.anum + 1), ' ; ', -1) AS result
FROM Catalogs
CROSS JOIN
(SELECT 1 AS anum UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) units
CROSS JOIN
(SELECT 1 AS anum UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) tens
WHERE LENGTH(value) - LENGTH(REPLACE(value, ';', '')) >= ( tens.anum * 10 + units.anum)
) sub0
GROUP BY result
The next example is my database.
tb_port
id port
1 80
2 22
3 53
4 3128
5 443
tb_dest
id dest
1 network
2 local
tb_rule
id id_port id_dest
1 1 1
2 2 1
3 3 1
4 4 1
5 5 1
Select:
select dest,group_concat(port) from tb_port a, tb_dest b, tb_rule c where a.id=c.id_port and b.id=c.id_dest group by dest
Result:
network 80,22,53,3128,443
but is not the result I'm looking for, the result would be this.
Select ex:
select dest,group_concat(port limit 2) from tb_port a, tb_dest b, tb_rule c where a.id=c.id_port and b.id=c.id_dest group by dest
result I would like
network 80,22
network 53,3128
network 443
how to achieve this result only with SQL?
Sqlfiddle: http://sqlfiddle.com/#!2/d11807
MySQL doesn't make this kind of query easy, but one (admittedly not very pretty) solution is to use a variable to give each row a sequence number per dest and just group by the row number integer divided by 2 to get two numbers in each group;
SELECT dest, GROUP_CONCAT(port ORDER BY rank) ports
FROM (
SELECT dest, port, (
CASE dest WHEN #curDest
THEN #curRow := #curRow + 1
ELSE #curRow := 1 AND #curDest := dest END) rank
FROM tb_port a
JOIN tb_rule c ON a.id = c.id_port
JOIN tb_dest b ON b.id = c.id_dest,
(SELECT #curRow := 0, #curDest := '') r
ORDER BY dest
) z
GROUP BY FLOOR(rank/2),dest
ORDER BY dest, MIN(rank)
An SQLfiddle to test with.
Here is a stored proc,you just put in the delimiter when you call it
DELIMITER $$
DROP PROCEDURE IF EXISTS explode_table $$
CREATE PROCEDURE explode_table(bound VARCHAR(255))
BEGIN
DECLARE id TEXT;
DECLARE value TEXT;
DECLARE occurance INT DEFAULT 0;
DECLARE i INT DEFAULT 0;
DECLARE splitted_value TEXT;
DECLARE done INT DEFAULT 0;
DECLARE cur1 CURSOR FOR
select dest,group_concat(port) from tb_port a, tb_dest b, tb_rule c
where a.id=c.id_port and b.id=c.id_dest and dest != '' group by dest;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
DROP TEMPORARY TABLE IF EXISTS table2;
CREATE TEMPORARY TABLE table2(
`id` VARCHAR(255),
`value` VARCHAR(255) NOT NULL
) ENGINE=Memory;
OPEN cur1;
read_loop: LOOP
FETCH cur1 INTO id, value;
IF done THEN
LEAVE read_loop;
END IF;
SET occurance = (SELECT LENGTH(CONCAT(value,bound))
- LENGTH(REPLACE(CONCAT(value,bound), bound, ''))
+1);
SET i=2;
WHILE i <= occurance DO
SET splitted_value =
SUBSTRING_INDEX(SUBSTRING_INDEX(CONCAT(value,bound),bound,i),bound,-2) ;
INSERT INTO table2 VALUES (id, splitted_value);
SET i = i + 2;
END WHILE;
END LOOP;
SELECT * FROM table2;
CLOSE cur1;
END; $$
CALL explode_table(',')
This question already has answers here:
Oracle Replace function
(3 answers)
Closed 9 years ago.
I need to replace the Table1's filed values from Table2's values while select query.
Eg:
Table1:
Org Permission
--------------------------------------
Company1 1,3,7
Company2 1,3,8
Table2:
Permission Permission
--------------------------------------
1 Read
3 Write
7 Execute
8 Delete
I need like this:
Org Permission
--------------------------------------
Company1 Read,Write,Execute
Company2 Read,Write,Delete
I have been following your post since it was tagged in Oracle :D
In oracle it was looking in much possible ways, But in mysql you have to follow it with the help of procedure:
Schema:
create table table1 (org varchar(50), permission_id varchar(50));
create table table2 (permission_id int, permission_name varchar(50));
insert into table1 values ('Company1','1,3,7'),('Company2','1,3,8');
insert into table2 values (1,'Read'),(3,'Write'),(7,'Execute'),(8,'Delete');
Procedure:
DELIMITER $$
DROP PROCEDURE IF EXISTS `Update_Table_data`$$
CREATE PROCEDURE `Update_Table_data`()
BEGIN
declare max_row int;
declare p1 int;
Set p1 = 0;
SET max_row = (SELECT max(#i:=#i+1) AS row_num FROM table2 AS t,(SELECT #i:=0) AS foo);
label1: LOOP
set p1 = p1 + 1;
IF p1 <= max_row THEN
UPDATE Table1
SET permission_id =
replace(permission_id, (select permission_id from
(SELECT #i:=#i+1 AS row_num ,t.* FROM table2 AS t,(SELECT #i:=0) AS foo) a
where row_num = p1),
(select permission_name from
(SELECT #i:=#i+1 AS row_num ,t.* FROM table2 AS t,(SELECT #i:=0) AS foo) a
where row_num = p1));
Iterate label1;
END IF;
LEAVE label1;
END LOOP label1;
-- SET #x = p1;
END$$
DELIMITER ;
and then make a call to update your values :
call Update_Table_data;
Hope it helps :)
See SQLFiddle for initial data and this is resultant data SQLFiddle
UPDATE
organization
SET
permisson = (SELECT
GROUP_CONCAT(VALUE)
FROM
( SELECT
org,
SUBSTRING_INDEX(permisson,',',1) AS `permisson`
FROM
organization
UNION
SELECT
org,
SUBSTRING_INDEX(SUBSTRING_INDEX(permisson,',',2),',',-1) AS `permisson`
FROM
organization
UNION
SELECT
org,
SUBSTRING_INDEX(permisson,',',-1) AS `permisson`
FROM
organization
) AS t
JOIN
permission p
WHERE
p.p_id = t.permisson AND
t.org = organization.org
GROUP BY org
)
Try to replace your numbers using REPLACE.It work properly only when you are using single digit values for permission.
SQL = " SELECT Org, REPLACE(REPLACE(REPLACE(REPLACE(Permission,"1","Read'"),"3","Write'"),"7","Execute'"),"8","Delete'") as Permission FROM myTable "
REPLACE()
I have the following SQL script. As you can see I manually set the #listingid value to 30653.
But this script should be executed for all records in the [listings] table where #listingid is assigned the value of the [listings].id column.
DECLARE #profname nvarchar(150)
DECLARE #furl nvarchar(250)
DECLARE #city nvarchar(250)
DECLARE #listingid int
set #listingid=30653
--select the top 1 professionname
SELECT TOP 1 #profname=REPLACE(LOWER(pn.title),' ','-'),#furl=l.friendlyurl,#city=REPLACE(REPLACE(LOWER(l.city),'''',''),' ','-') FROM healthprof_professionnames hpn
INNER JOIN professionname pn ON pn.id=hpn.professionnameid
INNER JOIN listings l on l.id=hpn.healthprofid
WHERE l.id=#listingid ORDER BY pn.title
--check if current friendlyurl already contains profession
IF NOT CHARINDEX(#profname,#furl)>0
SET #furl = #furl + '-' + #profname
IF NOT CHARINDEX(#city,#furl)>0
SET #furl = #furl + '-' + #city
SET #furl = #furl + '-3'
UPDATE listings set friendlyurl=#furl WHERE id=#listingid
You can use a cursor to loop over every row in a result set:
declare cur cursor for
select distinct id from listings
declare #listingid int
open cur
fetch next from cur into #listingid
while ##FETCH_STATUS = 0
BEGIN
-- your code from above goes here
fetch next from cur into #listingid
END
That being said, I agree with Tim's comment above. Rewrite it to work in one set-based operation if at all possible. I think this will work, but I haven't tested it:
;WITH vars AS (
SELECT id, profname, furl, city
FROM (
SELECT l.id,
REPLACE(LOWER(pn.title),' ','-') as profname,
l.friendlyurl as furl,
REPLACE(REPLACE(LOWER(l.city),'''',''),' ','-') as city,
ROW_NUMBER() OVER (PARTITION BY l.id ORDER BY pn.title) as rnk
FROM healthprof_professionnames hpn
INNER JOIN professionname pn ON pn.id=hpn.professionnameid
INNER JOIN listings l on l.id=hpn.healthprofid
) A
WHERE A.rnk = 1
),
vars2 AS (
SELECT id,
CASE WHEN NOT CHARINDEX(profname, furl) > 0
THEN furl + '-' + profname ELSE furl END as furl,
city
FROM vars
),
vars3 as (
SELECT id,
CASE WHEN NOT CHARINDEX(city, furl) > 0
THEN furl + '-' + city ELSE furl END as furl
FROM vars2
)
UPDATE listings SET friendlyurl = vars3.furl + '-3'
FROM listings INNER JOIN vars3 on vars3.id = listings.id
I have a table which has columns UserID,DIFFTIME. when I select these columns from the table, I also want to have a derived column which is : If the DiffTime is > 20 I want to increment the count per user id.
For example if the table has:
User ID DIFF TIME
1 0
1 5
1 10
2 0
2 21
2 5
I want a result set that is something like this:
User ID DIFF TIME SESSION NUMBER
1 0 1
1 5 1
1 10 1
2 0 1
2 21 2
2 5 2
How do I accomplish this.
Ideas and suggestions are much appreciated!
Use this statement:
select t1.User_Id, t1.Diff_Time,
isnull(count(t2.User_Id), 0) + 1 as Session_Number
from #table t1
left join #table t2
on t1.User_Id = t2.User_Id
and t1.eventTime >= t2.eventTime
and t2.Diff_Time > 20
group by t1.User_Id, t1.Diff_Time, t1.eventTime
order by t1.User_Id, t1.eventTime
(replace #table with your actual table name)
Note: I assume that the fifth row of your table has the value 21 in the Diff_Time column, and there's a typo in the question, as #AaronBertrand pointed out in the comments
create table #t
(
id int,
Diff int,
SessionNumber int
)
insert into #t(id, diff)values(1, 0)
insert into #t(id, diff)values(1, 5)
insert into #t(id, diff)values(1, 10)
insert into #t(id, diff)values(2, 0)
insert into #t(id, diff)values(2, 21)
insert into #t(id, diff)values(2, 5)
Select ROW_NUMBER() over(order by Id) as RowID, * into #Temp1 from #t
Declare #diff int
Declare #RowId int
Declare #Previous int
Declare #NewValue int
DECLARE #Cur CURSOR SET #Cur = CURSOR FOR select RowId, diff from #Temp1
OPEN #Cur
FETCH NEXT FROM #Cur INTO #RowId, #diff
WHILE ##FETCH_STATUS = 0
BEGIN
if(#RowId = 1)
Begin
Update #Temp1 Set SessionNumber = 1 Where rowid = 1
Set #Previous = #Diff
Set #NewValue = 1
End
Else
Begin
if(#Diff - #Previous > 20)
Begin
Set #Previous = #Diff
Set #NewValue = #NewValue + 1
Update #Temp1 Set SessionNumber = #NewValue Where rowid = #RowId
End
else
Update #Temp1 Set SessionNumber = #NewValue Where rowid = #RowId
End
FETCH NEXT FROM #Cur INTO #RowId, #diff
END
CLOSE #Cur
DEALLOCATE #Cur
select * from #temp1
drop table #t
drop table #temp1