Comma separated Values - sql

How can i fetch this query using mysql?
Table1:
id : nos
1 12,13,14
2 14
3 14,12
Table2:
id : values
12 PHP
13 JAVA
14 C++
Now , I want output like this:
1 PHP, JAVA, C++
2 C++
3 C++, PHP

Not tested but it should be something like this:
SELECT table1.id, GROUP_CONCAT(table2.values)
FROM table1 INNER JOIN table2 ON FIND_IN_SET(table2.id, table1.nos)
GROUP BY table1.id

There's no way that I know of to achieve that in SQL. You should instead have a 1 to N relationship to represent those lists. Something like:
Table 1: (just ids)
1
2
3
Table 1.1: (map ids to values in their list)
1, 12
1, 13
1, 14
2, 14
3, 14
3, 12

Not sure if this will work in mySQL but in SqlServer you could create a function:
create function dbo.replaceIdsWithValues
(
#inIds varchar(50)
)
returns varchar(50)
as
begin
declare #ret as varchar(50)
set #ret = #inIds
select #ret = replace(#ret,cast(id as varchar),theValues) from t2
return #ret
end
and then simply call:
select id, nos, dbo.replaceIdsWithValues(nos) from t1
that assuming your tables structure:
create table t1 (id int, nos varchar(50))
create table t2 (id int, theValues varchar(50))
You can test the full example
create table t1 (id int, nos varchar(50))
create table t2 (id int, theValues varchar(50))
insert into t1(id, nos)
select 1, '12,13,14'
union all select 2, '14'
union all select 3, '14,12'
insert into t2(id, theValues)
select 12, 'PHP'
union all select 13, 'JAVA'
union all select 14, 'C++'
select id, nos, dbo.replaceIdsWithValues(nos) from t1

Intended this as comment but it is getting long.
SoulMerge answer(+1) is specific to MySql, which the question was intially intended. Please see the edits for the initial question.
Seems the question again got edited for the MY-SQL, but anyway.
While you can achieve this in MS SQL by using PATINDEX, I am not sure you can do it this in oracle.
I think it would be better to restructure the tables as suggested by jo227o-da-silva(+1).

Although not completely relevant to the subject (MySQL), but will help others finding the question by title, in MSSQL server this can be achived using the FOR XML hint and some nasty string replacements.
I'll post up some code when I find it...

Related

SQL Passing tables as parameters?

I am performing a multiplication that requires two columns from two different tables, and then using the results in a separate query. I think this would be possible as a view:
SELECT SUM(A.salesAmt * B.sales%) AS rebateAmount
But would a table-valued function be possible here, or more appropriate? Could both table A and B be passed as parameters to return the final rebateAmount as a sum?
Table A contains: salesID, salesAmt
Table B contains: salesID, sales%
Would like the TVF to return the sum of (salesAmt * sales%) as rebateAmount if possible? This is SQL Server 2014.
Thanks in advance.
Passing a table as a parameter doesn't seems to resolve your issue.
Instead there seems to be a simpler way:
-- create temp tables
Create Table #a (salesID int, salesAmt decimal(15,2) )
Create Table #b (salesID int, salesPerc decimal(5,2) )
-- insert data
Insert into #a
Select 1, 2567.34
Union
Select 2, 335.57
Union
Select 3, 95.35
Union
Select 4, 303.83
Union
Select 5, 743.66
-- insert data
Insert into #b
Select 1, 4.5
Union
Select 2, 10.0
Union
Select 3, 2.5
Union
Select 4, 6.0
Union
Select 5, 20.0
-- to get the data of individual columns + the multiple of the salesAmt & salesPerc
Select a.*, b.salesPerc, convert(decimal(15,2), a.salesAmt * b.salesPerc ) as Mult from #a a inner join #b b on a.salesID = b.salesID
-- to get the sum of the multiple of the salesAmt & salesPerc
Select Sum (convert(decimal(15,2), a.salesAmt * b.salesPerc )) as SumOfMult from #a a inner join #b b on a.salesID = b.salesID

MS SQL sorting a column with Chinese Name

I have a database table which has English and Chinese name in them. I have changed the Chinese name column's collation to Chinese_PRC_CS_AS but it is still not sorting properly.
I would like them to sort according to how the dictionary using (pinyin).
May I know if anybody has done that before?
You can specify Chinese_PRC_CS_AS in your select statement
select * from yourtable order by columnWithChineseName collate Chinese_PRC_CI_AS
I also found the solution at [http://www.blogjava.net/parable-myth/archive/2010/10/12/334525.html][1]. To prevent the link is broken in the future, I will paste the content here. Credit should be given back to the original author.
sql按拼音排序
select * from user order by name collate Chinese_PRC_CS_AS_KS_WS
二.排序规则简介:
什么叫排序规则呢?ms是这样描述的:"在 microsoft sql server 2000 中, 字符串的物理存储由排序规则控制。排序规则指定表示每个字符的位模式以及存 储和比较字符所使用的规则。"
在查询分析器内执行下面语句,可以得到sql server支持的所有排序规则。
select * from ::fn_helpcollations()
排序规则名称由两部份构成,前半部份是指本排序规则所支持的字符集。 如: chinese_prc_cs_ai_ws
前半部份:指unicode字符集,chinese_prc_指针对大陆简体字unicode的排序规则。 排序规则的后半部份即后缀 含义:
_bin 二进制排序
_ci(cs) 是否区分大小写,ci不区分,cs区分
_ai(as) 是否区分重音,ai不区分,as区分   
_ki(ks) 是否区分假名类型,ki不区分,ks区分 
_wi(ws) 是否区分宽度 wi不区分,ws区分 
区分大小写:如果想让比较将大写字母和小写字母视为不等,请选择该选项。
区分重音:如果想让比较将重音和非重音字母视为不等,请选择该选项。如果选择该选项,
比较还将重音不同的字母视为不等。 区分假名:如果想让比较将片假名和平假名日语音节视为不等,请选择该选项。 区分宽度:如果想让比较将半角字符和全角字符视为不等,请选择该选项
三.排序规则的应用: sql server提供了大量的windows和sqlserver专用的排序规则,但它的应用往往
被开发人员所忽略。其实它在实践中大有用处。
例1:让表name列的内容按拼音排序:
create table #t(id int,name varchar(20)) insert #t select 1,中 union
all select 2,国 union all select 3,人 union all select 4,阿
select * from #t order by name collate chinese_prc_cs_as_ks_ws drop
table #t /*结果: id name
----------- -------------------- 4 阿 2 国 3 人 1 中
*/
例2:让表name列的内容按姓氏笔划排序:
create table #t(id int,name varchar(20))
insert #t select 1,三 union all select 2,乙 union all select 3,二 union
all select 4,一 union all select 5,十 select * from #t order by name
collate chinese_prc_stroke_cs_as_ks_ws drop table #t /*结果: id
name
----------- -------------------- 4 一 2 乙 3 二 5 十 1 三
*/
四.在实践中排序规则应用的扩展 sql server汉字排序规则可以按拼音、笔划等排序,那么我们如何利用这种功能
来处理汉字的一些难题呢?我现在举个例子:
用排序规则的特性计算汉字笔划
要计算汉字笔划,我们得先做准备工作,我们知道,windows多国汉字,unicode目前
收录汉字共20902个。简体gbk码汉字unicode值从19968开始。
首先,我们先用sqlserver方法得到所有汉字,不用字典,我们简单利用sql语句就 可以得到:
select top 20902 code=identity(int,19968,1) into #t from syscolumns
a,syscolumns b
再用以下语句,我们就得到所有汉字,它是按unicode值排序的:
select code,nchar(code) as cnword from #t
然后,我们用select语句,让它按笔划排序。
select code,nchar(code) as cnword from #t order by nchar(code)
collate chinese_prc_stroke_cs_as_ks_ws,code
结果: code cnword
本文章出处 http://www.itphome.cn/shujukuyingyong/mssql/2010-01-27/106.html
[1]: http://www.itphome.cn/shujukuyingyong/mssql/2010-01-27/106.html

Insert missing values from table

I have a table with a PK that grows fairly quickly, but since rows are fairly consistently deleted, it becomes a very sparse table quickly as such:
ID VALUE
----------------
1 'Test'
5 'Test 2'
24 'Test 3'
67 'Test 4'
Is there a way that I can automatically insert the next value in the missing IDs so that I don't grow that ID extremely large? For example, I'd like to insert 'Test 5' with ID 2.
I wouldn't do that.
As already explained by others in the comments, you gain nothing by re-filling gaps in the numbers.
Plus, you might even unintentionally mess up your data if you refer to these IDs anywhere else:
Let's say that there once was a row with ID 2 and you deleted it.
Then you insert a complete new row and re-use ID 2.
Now if you have any data anywhere that references ID 2, it suddenly links to the new value instead of the old one.
(Note to nit-pickers: Yes, this should not happen if referential integrity is set up properly. But this is not the case everywhere, so who knows...)
I'm not suggesting doing what you're trying to do, but if you want to do it, this is how. I am only answering the question, not solving the problem.
In your proc, you'd what to lock your table while doing this so that you don't get one the sneaks in. By using something link this:
EXEC #result = sp_getapplock #Resource = #LockResource,
#LockMode = 'Exclusive'
AND
EXEC sp_releaseapplock #Resource = #LockResource
TABLE
DECLARE #table TABLE ( id INT, val VARCHAR(20) )
DATA
INSERT INTO #table
(
id,
val
)
SELECT 1,
'Test'
UNION ALL
SELECT 2,
'Test'
UNION ALL
SELECT 5,
'Test 2'
UNION ALL
SELECT 24,
'Test 3'
UNION ALL
SELECT 67,
'Test 4'
Queries
INSERT INTO #table
SELECT TOP 1
id + 1,
'TEST'
FROM #table t1
WHERE NOT EXISTS ( SELECT TOP 1
1
FROM #table
WHERE id = t1.id + 1 )
ORDER BY id
INSERT INTO #table
SELECT TOP 1
id + 1,
'TEST'
FROM #table t1
WHERE NOT EXISTS ( SELECT TOP 1
1
FROM #table
WHERE id = t1.id + 1 )
ORDER BY id
SELECT *
FROM #table
RESULT
id val
1 Test
2 Test
5 Test 2
24 Test 3
67 Test 4
3 TEST
4 TEST
I deleted my answer about identity since they are not involved. It would be interesting to see if you are using this as a clustered index key, since to fill in gaps would violate the rule of thumb of strictly increasing values.
To just fill in gaps is relatively simple with a self-join and since you have a primary key, this query should run quickly to find the first gap (but of course, how are you handling simultaneous inserts and locks?):
SELECT lhs.ID + 1 AS firstgap
FROM tablename AS lhs
LEFT JOIN tablename AS rhs
ON rhs.ID = lhs.ID + 1
WHERE rhs.ID IS NULL
And inserting batches of records requires each insert to be done separately, while IDENTITY can handle that for you...
As said before: don't worry about the unused ID's.
It is however good practise to optimize the table when a lot of deletes happen.
In MySQL you can do this with:
optimize table tablename

How to get result of LEFT join into another table as one field

Not sure how to describe this so I will show example:
table PAGES
id int
parent int
name nvarchar
status tinyint
table PAGES_MODULES
id int
id_parent int
module_type nvarchar
module_id int
status int
One page can have more than one linked modules. Example records:
id parent name status
1 -1 Xyz 1
2 -1 Yqw 1
id id_parent module_type module_id status
1 1 ARTICLE 1 1
2 1 GALLERY 2 1
3 2 CATEGORY 3 1
What I need is to create select which will not return 2 results if I do select left join page_modules.
I would like to have select which returns linked modules as this:
id parent name status modules
1 -1 Xyz 1 ARTICLE GALLERY
2 -1 Yqw 1 CATEGORY
Is that possible?
Thanks.
UPDATE
I have tried COALESE, CROSS APPLY and SELECT within SELECT methods and came to these conclusions:
http://blog.feronovak.com/2011/10/multiple-values-in-one-column-aka.html
Hope I can publish these here, not meaning to spam or something.
You'd need to create a custom aggregate function that could concatenate the strings together, there is no built-in SQL Server function that does this.
You can create a custom aggregate function (assuming your using the latest version of SQL) using a .Net assembly. Here's the MS reference on how to do this (the example in the article is actually for a CONCATENATE function just like you require): http://msdn.microsoft.com/en-us/library/ms182741.aspx
Use group_concat() to smoosh multiple rows' worth of data into a single field like that. Note that it does have a length limit (1024 chars by default), so if you're going to have a zillion records being group_concatted, you'll only get the first few lines worth unless you raise the limit.
SELECT ..., GROUP_CONCAT(modules SEPARATOR ' ')
FROM ...
GROUP BY ...
Note that it IS an aggregate function, so you must have a group-by clause.
-- ==================
-- sample data
-- ==================
declare #pages table
(
id int,
parent int,
name nvarchar(max),
status tinyint
)
declare #pages_modules table
(
id int,
id_parent int,
module_type nvarchar(max),
module_id int,
status int
)
insert into #pages values (1, -1, 'Xyz', 1)
insert into #pages values (2, -1, 'Yqw', 1)
insert into #pages_modules values (1, 1, 'ARTICLE', 1, 1)
insert into #pages_modules values (2, 1, 'GALLERY', 2, 1)
insert into #pages_modules values (3, 2, 'CATEGORY', 3, 1)
-- ==================
-- solution
-- ==================
select
*,
modules = (
select module_type + ' ' from #pages_modules pm
where pm.id_parent = p.id
for xml path('')
)
from #pages p
You need to join both tables and then GROUP BY by pages.id, pages.parent, pages.status, pages.name and pages.status. Your modules field in your resultset is then a string aggregate function, i.e in Oracle LISTAGG(pages_modules.modules, ' ') as modules.

How can I use the values Selected in a While Exists statement inside the While loop?

I'm new-ish to SQL and am trying to figure out how to use the values from the Select statement in a While Exists conditional loop. The purpose is to combine multiple occurences of an attribute for a Document into a single field, and later pivot and join those results to the Document record.
For example, three tables exist like so:
ATTRIBUTES TABLE
ID, ATTRIBUTE_NAME
---------------------------
1, Created
2, Embedded_Image
...
ATTRIBUTE_VALUES TABLE
ATTRIBUTE_ID, VALUE, DOC_ID
-------------------------------------------
1, 2010/11/01, 1
2, 'Home.png', 1
2, 'Castle.png', 1
2, 'Apartment.jpg', 1
1, 2008/06/23, 2
2, 'Ski Jump.jpg', 2
2, 'Snowboarding.png', 2
...
DOCUMENTS TABLE
ID, TEXT
---------------------------
1, 'Homes of the ...'
2, 'Winter sports ...'
...
So a final Pivot and Join of the tables would look like so:
DOC_ID, TEXT, Created, Embedded_Image
----------------------------------------------------------------------------------------
1, 'Homes of the ...', 2010/11/01, 'Home.png,Castle.png,Apartment.jpg'
2, 'Winter sports ...', 2008/06/23, 'Ski Jump.jpg, Snowboarding.png'
The SQL While Exists condition I've tried to write looks like so:
DECLARE #LOOP_DOC_ID UNIQUEIDENTIFIER
DECLARE #LOOP_ATTRIBUTE_NAME NVARCHAR(MAX)
WHILE EXISTS(
SELECT [dbo].[ATTRIBUTE_VALUES].[DOC_ID], [dbo].[ATTRIBUTES].[ATTRIBUTE_NAME]
FROM ([dbo].[ATTRIBUTE_VALUES] INNER JOIN [dbo].[ATTRIBUTES]
ON [dbo].[ATTRIBUTE_VALUES].[ATTRIBUTE_ID] = [dbo].[ATTRIBUTES].[ID])
)
BEGIN
SET #LOOP_DOC_ID = DOC_ID
SET #LOOP_ATTRIBUTE_NAME = ATTRIBUTE_NAME
SELECT STUFF(
(
SELECT DISTINCT ',' + RTRIM(LTRIM([dbo].[ATTRIBUTE_VALUES].[VALUE]))
FROM
(
[dbo].[ATTRIBUTE_VALUES] INNER JOIN [dbo].[ATTRIBUTES]
ON [dbo].[ATTRIBUTE_VALUES].[ATTRIBUTE_ID] = [dbo].[ATTRIBUTES].[ID]
)
WHERE [dbo].[ATTRIBUTE_VALUES].[DOC_ID] = #LOOP_DOC_ID
AND [dbo].[ATTRIBUTES].[ATTRIBUTE_NAME] = #LOOP_ATTRIBUTE_NAME
ORDER BY ',' + RTRIM(LTRIM([dbo].[ATTRIBUTE_VALUES].[VALUE]))
FOR XML PATH('')
), 1, 2, ''
) AS VALUE, #LOOP_DOC_ID AS DOC_ID, #LOOP_ATTRIBUTE_NAME AS ATTRIBUTE_NAME
END
SQL Server doesn't like the lines where I'm trying to SET the variables to the values from the Select statement in the While Exists condition.
How can I use the [dbo].[ATTRIBUTE_VALUES].[DOC_ID], [dbo].[ATTRIBUTES].[ATTRIBUTE_NAME] values Selected in the While Exists conditional statement between the BEGIN and END statements?
Preferrably I would like to do away with the #LOOP_DOC_ID and #LOOP_ATTRIBUTE_NAME variables and deal directly with the values.
I've looked through forums that have talked about using Cursors to solve similar problems, but each one of them seem to recommend only using Cursors as a last resort due to their lack of speed. I've also seen some people use stored procedures, but I can't use those, since my boss has ruled those as off-limits. Am I in need of a Cursor, or is there a better way to do this?
Have a look at something like this (Full Example)
DECLARE #ATTRIBUTES TABLE(
ID INT,
ATTRIBUTE_NAME VARCHAR(100)
)
INSERT INTO #ATTRIBUTES SELECT 1, 'Created'
INSERT INTO #ATTRIBUTES SELECT 2, 'Embedded_Image'
DECLARE #ATTRIBUTE_VALUES TABLE(
ATTRIBUTE_ID INT,
VALUE VARCHAR(100),
DOC_ID INT
)
INSERT INTO #ATTRIBUTE_VALUES SELECT 1, '2010/11/01', 1
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Home.png', 1
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Castle.png', 1
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Apartment.jpg', 1
INSERT INTO #ATTRIBUTE_VALUES SELECT 1, '2008/06/23', 2
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Ski Jump.jpg', 2
INSERT INTO #ATTRIBUTE_VALUES SELECT 2, 'Snowboarding.png', 2
DECLARE #DOCUMENTS TABLE(
ID INT,
[TEXT] VARCHAR(100)
)
INSERT INTO #DOCUMENTS SELECT 1, 'Homes of the ...'
INSERT INTO #DOCUMENTS SELECT 2, 'Winter sports ...'
;WITH Vals AS (
SELECT d.ID DOC_ID,
d.[TEXT] [TEXT],
a.ATTRIBUTE_NAME,
av.VALUE
FROM #DOCUMENTS d INNER JOIN
#ATTRIBUTE_VALUES av ON d.ID = av.DOC_ID INNER JOIN
#ATTRIBUTES a ON av.ATTRIBUTE_ID = a.ID
)
SELECT *
FROM (
SELECT DOC_ID,
[TEXT],
ATTRIBUTE_NAME,
stuff(
(
select ',' + t.VALUE
from Vals t
where t.DOC_ID = v.DOC_ID
AND t.ATTRIBUTE_NAME = v.ATTRIBUTE_NAME
order by t.VALUE
for xml path('')
),1,1,'') Concats
FROM Vals v
GROUP BY DOC_ID,
[TEXT],
ATTRIBUTE_NAME
) s
PIVOT ( MAX(ConCats) FOR ATTRIBUTE_NAME IN ([Created],[Embedded_Image])) pvt
Output
DOC_ID TEXT Created Embedded_Image
1 Homes of the ... 2010/11/01 Apartment.jpg,Castle.png,Home.png
2 Winter sports ... 2008/06/23 Ski Jump.jpg,Snowboarding.png
From your sample, and with support from common sense, I venture the hypothesis that
A document has a single creation date.
A document can have many embedded images.
So pivoting on creation date is straightforward:
SELECT DOC_ID
, VALUE AS Created
FROM ATTRIBUTE_VALUES
WHERE ATTRIBUTE_ID = 1
and joining this subquery to your Documents table gives you the first three columns of your desired output.
Your final column summarizes multiple embedded images for each document. I personally would use some standard reporting tool (e.g. MS Access or Crystal Reports). Alternatively, create a new empty table with your four desired columns, populate the first three columns with a SQL INSERT statement, and then have Perl (or C#, or your favorite declarative language) query for the embedded images of each document, concatenate the results with commas, and insert the concatenation into your fourth column.
But if you want to do it in SQL, the concatenate-multiple-values question has been asked here before, e.g. in How to create a SQL Server function to "join" multiple rows from a subquery into a single delimited field?.