How to transform vertical data into horizontal data with SQL? - sql

I have a table "Item" with a number of related items, like so:
ID Rel_ID Name RelRank
--- ------ ---- -------
1 1 foo 1
2 1 bar 2
3 1 zam 3
4 2 foo2 1
I'm trying to get a query so items with the same Rel_ID would appear in the same row, like so:
Rel_ID Name1 Name2 Name3
------ ----- ----- -----
1 foo bar zam
2 foo2
I've tried selecting the table multiple times:
SELECT k.Rel_ID, k.name 'Name1', k2.name 'Name2'
FROM item k, item k2
WHERE k.Rel_ID = k2.Rel_ID
But this fails. Surely there's a transformation or query that could drastically simplify the process, and I'm just missing it because I haven't used SQL in this way before. What am I missing?
[Edit: added RelRank column, which does appear in my data]

Regardless of the database you are using, the concept of what you are trying to achieve is called "Pivot Table".
Here's an example for mysql:
http://en.wikibooks.org/wiki/MySQL/Pivot_table
Some databases have builtin features for that, see the links below.
SQLServer:
http://msdn.microsoft.com/de-de/library/ms177410.aspx
Oracle:
http://www.dba-oracle.com/t_pivot_examples.htm
You can always create a pivot by hand. Just select all the aggregations in a result set and then select from that result set. Note, in your case, you can put all the names into one column using concat (i think that's group_concat in mysql), since you cannot know how many names are related to a a rel_id.
pseudo-select for your case (i don't know mysql):
select rel_id, group_concat(name) from item group by rel_id

I think you are looking for a mysql specific answer.
Keep in mind that the syntax could vary across different data stores.
MySQL has a feature that makes this easy.
SELECT Rel_ID, GROUP_CONCAT(Name SEPARATOR ' ') As Names FROM Item GROUP BY Rel_ID;
that should work :-)

if the names that you listed are static,my below query that i runned sucessfully in sqlfiddle will work
SELECT rel_id,
MAX (DECODE (rel_id, '1', DECODE (relrank, '1', name) , '2',DECODE (relrank, '1', name))) NAME1,
MAX (DECODE (rel_id, '1', DECODE (relrank, '2', name))) NAME2,
MAX (DECODE (rel_id, '1', DECODE (relrank, '3', name))) NAME3
FROM supportContacts
GROUP BY rel_id
heres the SQL fiddle
http://sqlfiddle.com/#!4/480e2/11

Related

To get different row values in different columns in a single row

My table looks like this:
Param_id Param_value
------------------------
A 1
B 2
C 3
D 4
.... and so on. Now I want only the values of Param_id "A" and "B".
Now I want to get the param_value in two different columns instead of two different rows. But if I use IN clause it will return the result in two rows.
I want something like the below:
Param_value_1 Param_value_2
---------------------------------
1 2
I can't use listagg or pivot because they are not serving my purpose. Is there any other way to achieve this? I searched in Google but could not find any solution for this.
The old way of pivoting... Since you are looking for the parameter values for parameter_id in ('A', 'B'), it doesn't make much sense to name the resulting columns param_value_1 and param_value_2; why not param_value_a and param_value_b? (Otherwise what determines that 'A' is 1 and 'B' is 2, and not the other way around?)
So - back to the old way of pivoting (although I suspect PIVOT will work too, regardless of requirement - unless you are on Oracle 10 or lower):
select max(case when param_id = 'A' then param_value end) as param_value_a,
max(case when param_id = 'B' then param_value end) as param_value_b
from your_table;

Return count of records in each row SQL

I have one table like this.
SQL> SELECT * FROM FRUIT;
F_NAME
----------
APPLE
PPPALEP
APLEE
PPAALLEEPP
ornPpfpP
PPdhppPP
Above one is my source table and I want to below output.If i am giving 'P' in multiform like including capital and small both.
I want to count only 'P' from each row.
OUTPUT
------
F_NAME COUNT
------ -----
APPLE 2
PPPALEP 4
APLEE 1
PPAALLEEPP 4
ornPpfpP 4
PPdhppPP 6
Thanks in advance.
Oracle has the very convenient regexp_count(). So:
select f_name, regexp_count(f_name, 'P') as cnt
from fruit;
You can count the number of occurrences by replacing P with blanks and subtracting the length of the replaced string from the original string.
select f_name,length(f_name)-length(replace(f_name,'P','')) cnt
from fruit
Edit: Per OP's comment, to count both P and p, use upper or lower when replacing the character with an empty string.
select f_name,length(f_name)-length(replace(upper(f_name),'P','')) cnt
from fruit

BigQuery SQL same column multiple expressions

I'm using Google's Big Query service to do some data processing...my database looks like:
value
-----
'a'
'b'
'a'
'a'
'a'
'b'
I want to write a query to count the occurrences of the various values.
Example:
Count('a') Count('b')
---------- ----------
4 3
I'd normally use Case to solve this; but BQ doesn't support Case.
Anyone have any ideas?
Thanks!
The first thing I would suggest is a group by:
select value, count(*)
from t
group by value
But you seem to want the values in one row. According to this documentation, it does support case. If you prefer, you can use if:
select sum(if(value = 'A', 1, 0)) as A, sum(if(value = 'B', 1, 0)) as B
from t

How to group by a column

Hi I know how to use the group by clause for sql. I am not sure how to explain this so Ill draw some charts. Here is my original data:
Name Location
----------------------
user1 1
user1 9
user1 3
user2 1
user2 10
user3 97
Here is the output I need
Name Location
----------------------
user1 1
9
3
user2 1
10
user3 97
Is this even possible?
The normal method for this is to handle it in the presentation layer, not the database layer.
Reasons:
The Name field is a property of that data row
If you leave the Name out, how do you know what Location goes with which name?
You are implicitly relying on the order of the data, which in SQL is a very bad practice (since there is no inherent ordering to the returned data)
Any solution will need to involve a cursor or a loop, which is not what SQL is optimized for - it likes working in SETS not on individual rows
Hope this helps
SELECT A.FINAL_NAME, A.LOCATION
FROM (SELECT DISTINCT DECODE((LAG(YT.NAME, 1) OVER(ORDER BY YT.NAME)),
YT.NAME,
NULL,
YT.NAME) AS FINAL_NAME,
YT.NAME,
YT.LOCATION
FROM YOUR_TABLE_7 YT) A
As Jirka correctly pointed out, I was using the Outer select, distinct and raw Name unnecessarily. My mistake was that as I used DISTINCT , I got the resulted sorted like
1 1
2 user2 1
3 user3 97
4 user1 1
5 3
6 9
7 10
I wanted to avoid output like this.
Hence I added the raw id and outer select
However , removing the DISTINCT solves the problem.
Hence only this much is enough
SELECT DECODE((LAG(YT.NAME, 1) OVER(ORDER BY YT.NAME)),
YT.NAME,
NULL,
YT.NAME) AS FINAL_NAME,
YT.LOCATION
FROM SO_BUFFER_TABLE_7 YT
Thanks Jirka
If you're using straight SQL*Plus to make your report (don't laugh, you can do some pretty cool stuff with it), you can do this with the BREAK command:
SQL> break on name
SQL> WITH q AS (
SELECT 'user1' NAME, 1 LOCATION FROM dual
UNION ALL
SELECT 'user1', 9 FROM dual
UNION ALL
SELECT 'user1', 3 FROM dual
UNION ALL
SELECT 'user2', 1 FROM dual
UNION ALL
SELECT 'user2', 10 FROM dual
UNION ALL
SELECT 'user3', 97 FROM dual
)
SELECT NAME,LOCATION
FROM q
ORDER BY name;
NAME LOCATION
----- ----------
user1 1
9
3
user2 1
10
user3 97
6 rows selected.
SQL>
I cannot but agree with the other commenters that this kind of problem does not look like it should ever be solved using SQL, but let us face it anyway.
SELECT
CASE main.name WHERE preceding_id IS NULL THEN main.name ELSE null END,
main.location
FROM mytable main LEFT JOIN mytable preceding
ON main.name = preceding.name AND MIN(preceding.id) < main.id
GROUP BY main.id, main.name, main.location, preceding.name
ORDER BY main.id
The GROUP BY clause is not responsible for the grouping job, at least not directly. In the first approximation, an outer join to the same table (LEFT JOIN below) can be used to determine on which row a particular value occurs for the first time. This is what we are after. This assumes that there are some unique id values that make it possible to arbitrarily order all the records. (The ORDER BY clause does NOT do this; it orders the output, not the input of the whole computation, but it is still necessary to make sure that the output is presented correctly, because the remaining SQL does not imply any particular order of processing.)
As you can see, there is still a GROUP BY clause in the SQL, but with a perhaps unexpected purpose. Its job is to "undo" a side effect of the LEFT JOIN, which is duplication of all main records that have many "preceding" ( = successfully joined) records.
This is quite normal with GROUP BY. The typical effect of a GROUP BY clause is a reduction of the number of records; and impossibility to query or test columns NOT listed in the GROUP BY clause, except through aggregate functions like COUNT, MIN, MAX, or SUM. This is because these columns really represent "groups of values" due to the GROUP BY, not just specific values.
If you are using SQL*Plus, use the BREAK function. In this case, break on NAME.
If you are using another reporting tool, you may be able to compare the "name" field to the previous record and suppress printing when they are equal.
If you use GROUP BY, output rows are sorted according to the GROUP BY columns as if you had an ORDER BY for the same columns. To avoid the overhead of sorting that GROUP BY produces, add ORDER BY NULL:
SELECT a, COUNT(b) FROM test_table GROUP BY a ORDER BY NULL;
Relying on implicit GROUP BY sorting in MySQL 5.6 is deprecated. To achieve a specific sort order of grouped results, it is preferable to use an explicit ORDER BY clause. GROUP BY sorting is a MySQL extension that may change in a future release; for example, to make it possible for the optimizer to order groupings in whatever manner it deems most efficient and to avoid the sorting overhead.
For full information - http://academy.comingweek.com/sql-groupby-clause/
SQL GROUP BY STATEMENT
SQL GROUP BY clause is used in collaboration with the SELECT statement to arrange identical data into groups.
Syntax:
1. SELECT column_nm, aggregate_function(column_nm) FROM table_nm WHERE column_nm operator value GROUP BY column_nm;
Example :
To understand the GROUP BY clauserefer the sample database.Below table showing fields from “order” table:
1. |EMPORD_ID|employee1ID|customerID|shippers_ID|
Below table showing fields from “shipper” table:
1. | shippers_ID| shippers_Name |
Below table showing fields from “table_emp1” table:
1. | employee1ID| first1_nm | last1_nm |
Example :
To find the number of orders sent by each shipper.
1. SELECT shipper.shippers_Name, COUNT (orders.EMPORD_ID) AS No_of_orders FROM orders LEFT JOIN shipper ON orders.shippers_ID = shipper.shippers_ID GROUP BY shippers_Name;
1. | shippers_Name | No_of_orders |
Example :
To use GROUP BY statement on more than one column.
1. SELECT shipper.shippers_Name, table_emp1.last1_nm, COUNT (orders.EMPORD_ID) AS No_of_orders FROM ((orders INNER JOIN shipper ON orders.shippers_ID=shipper.shippers_ID) INNER JOIN table_emp1 ON orders.employee1ID = table_emp1.employee1ID)
2. GROUP BY shippers_Name,last1_nm;
| shippers_Name | last1_nm |No_of_orders |
for more clarification refer my link
http://academy.comingweek.com/sql-groupby-clause/

grouping items in sql query

I have a table with columns like (in sql server 2000)
MailCode Mode Name Group
-------- ----- --------- -------
1 1 abc 0
1 1 def 0
1 1 qwe 1
2 2 aaw 0
2 2 aad 0
I want to group the Name field based on the rest of the fileds so that the result looks like this (there should be only one unique mailCode, Mode and group combination)
MailCode Mode Names Group
--------- ------ ------------ -------
1 1 abc, def 0
1 1 qwe 1
2 2 aaw, aad 0
How can I create the sql query for this?
I had a similar problem where I had to concatenate a field in the select, my solution at the time was to create a procedure that returned the result and called it like this
select x as field1, y as field2, dbo.procedure as field3
SQL Server 2000 solution
Luckily, COALESCE is supported in 2000, so you can use the COALESCE trick to create a comma delimited list of values, demonstrated in this link. Because of the variable usage, you'll need to create a function/procedure and call it within the main query. Basically, just replace the STUFF() in the query below with the function call.
SQL Server 2005+ solution:
SELECT x.mailcode,
x.mode,
STUFF((SELECT y.name
FROM TABLE y
WHERE y.mailcode = x.mailcode
AND y.mode = x.mode
AND y.gropu = x.group
GROUP BY y.mailcode, y.mode, y.group
FOR XML PATH(', ')), 1, 1, '') AS name,
x.group
FROM TABLE x
GROUP BY x.mailcode, x.mode, x.group
I can't think of a simple query that will get the result you're looking for, but some logic along these lines should get you where you want:
1) Loop through distinct MailCode, Mode, Group Rows
A) select all names in group
A.1) Loop through names
A.2) Concatenate them together into temp variable
B) insert all data (MailCode, Mode, Group, temp variable) into temp table
Fair waring, looping in SQL tends to have a huge performance hit when it comes to large datasets. I unfortunately don't know a better way to do it.