I have a table named Inventory with the following structure:
Location_ID |Item_ID |Stock
1 |A |100
1 |B |500
1 |C |300
2 |A |10
2 |B |20
field location_ID and item_ID are composite key. I want to produce the following data from that single table:
Item_ID |Stock_1 |Stock_2
A |100 |10
B |500 |20
C |300 |0
I tried writing several self join queries but it doesn't work. There is also another problem: Item_ID C does not exist on location_ID 2. How can we put the value '0' on the resulting table if it does not exist? Can someone with brighter mind shed any light?
select DIS_ITME_ID,
IFNULL ((select stock
from inventory
where location_id = 1
and item_id = DIS_ITEM_ID), 0) as stock_1,
IFNULL ((select stock
from inventory
where location_id = 2
and item_id = DIS_ITEM_ID), 0) as stock_2
from (select distinct item_ID as DIS_ITEM_ID from inventory)
I know it's probably too late but there is a simpler way:
SELECT Item_Id,
SUM(
CASE WHEN Location_ID = 1 THEN
Stock
ELSE
0
END) As Stock1,
SUM(
CASE WHEN Location_ID = 2 THEN
Stock
ELSE
0
END) As Stock2
FROM Inventory
GROUP BY Item_Id
sqlfiddle
You can use a dinamic pivot query:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(IF(i.location_ID = ''',
CAST(location_ID as CHAR(50)),
''', i.stock, 0)) AS stock_',
CAST(location_ID as CHAR(50))
)
) INTO #sql
FROM inventory;
SET #sql = CONCAT('SELECT i.item_ID, ', #sql, '
FROM inventory i
GROUP BY i.item_ID');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
You can find more info here: http://buysql.com/mysql/14-how-to-automate-pivot-tables.html
Related
I have a MariaDB table with data like so:
Name | Value
John | 20
Adrian | 22
Tommy | 19
John | 24
John | 18
Adrian | 23
It is to be returned like so:
Name |Value1 |Value2 |Value3
John |20 |24 |18
Adrian |22 |23
Tommy |19
We will not know how many value columns there could be. I would prefer the solution to be SQL only. Grateful for any form of help! :)
Use group_concat
SELECT
Name,
GROUP_CONCAT(Value) 'Values'
FROM table_name
GROUP BY Name
ORDER BY Name;
Which will print all the values found in the second column.Its not exactly the presentation you asked for but it does give you the information in nearly the same format with a simple query.
If you need to evaluate the data there are many standard functions that you could use including:
AVG(Value)
COUNT(Value)
MAX(Value)
MIN(Value)
AVG(Value)
The alternative is a procedure which will be more complicated.
Is very hard to do in sql, because GROUP BY requires aggegate functions which distort the desired results. You could try make the column names the values and add a flag with a dynamic pivot.
Fiddle
create table t
(
name varchar(10),
value int
);
insert into t values
( 'John', 20),
( 'Adrian ', 22),
( 'Tommy ', 19),
( 'John ', 24),
( 'John ', 18),
( 'Adrian ', 23);
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(name = ''',
name,
''', 1, NULL)) AS `',
value,'`'
)
) INTO #sql
FROM t;
SET #sql = CONCAT('SELECT
t.name,
', #sql, '
FROM t
GROUP BY name ORDER BY name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
name 22 23 18 24 20 19
Adrian 1 1
John 1 1 1
Tommy 1
I have 2 tables:
table1:
|id|create_date|title |type |
-------------------------------
| 1|2015-07-20 |bla-bla|TICKET|
table2_meta:
|id|table1_id|meta_name|meta_value|
-----------------------------------
| 1| 1 | status | new |
| 2| 1 | priority| low |
| 3| 1 | owner | alex |
Now, i wish to select it like this:
|id|create_date|title |status|priority|owner|
|1 |2015-07-20 |bla-bla|new |low |alex |
My solution is:
SELECT
t1.id,
t1.event_date,
case when m.meta_name="status" then m.meta_value end as status,
case when m.meta_name="priority" then m.meta_value end as priority,
case when m.meta_name="owner" then m.meta_value end as owner
FROM table1 as t1
LEFT JOIN table2_meta as m
on m.table1_id=t1.id
WHERE t1.type='TICKET'
GROUP BY t1.id
So, it works. But seems a litle bit ugly.
My question is:
Are there any other solutions to this select and how to make it more productive?
Hey try this in your case for pivoting of table SQLFIDDLE
set #sql = null;
select
group_concat(distinct
concat(
'max(case when meta_name = ''',
meta_name,
''' then value end) AS ',
concat(meta_name)
)
) into #sql
FROM table1 as t1
LEFT JOIN table2 as m
on m.table1_id=t1.id
WHERE t1.type='TICKET';
set #sql = concat('select t1.id,', #sql, ' FROM table1 as t1
LEFT JOIN table2 as m
on m.table1_id=t1.id
WHERE t1.type=''TICKET''
GROUP BY t1.id
');
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;
In oracle you could:
Instead of case when you could use pivot.
SELECT * FROM table2
PIVOT (
meta_value FOR (meta_name) IN ('Status' AS status, 'Priority' AS priority, 'owner' AS Ownser))
i have the following tables:
**entries**
entry_id | date | engineer | project
**entries_allowanes_map**
entry_id | allowance_id
**allowances**
allowance_id | allowance_name
I want to create a SELECT query that will give the following result:
entry_id | date | engineer | project | allowance_name1 | allowance_name2 | allowance_name_n...
The queries I have tried return a row for each allowance an entry has registered with. I want just one row with all allowances attached to it.
Thanks in advance
I would propose doing this with group_concat(). It doesn't put the values in separate columns, but it does put everything for a given entry on one row:
select e.entry_id, e.date, e.engineer, e.project,
group_concat(a.allowance_name) as allowances
from entries e join
entries_allowances_map f
on e.entry_id = eam.entry_id
allowances a
on eam.allowance_id = a.allowance_id
group by e.entry_id;
Here is the query that I got:
It outputs your expected results in different columns:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'(SELECT max(CASE WHEN AL.ALLOWANCE_ID = ''',
ALLOWANCE_ID,
''' THEN 1 END) AS `',
ALLOWANCE_ID, '` FROM entries_allowanes_map AL WHERE E.ENTRY_ID = AL.ENTRY_ID ) AS `',
ALLOWANCE_NAME, '`'
)
) INTO #sql
FROM allowances;
SET #sql
= CONCAT('SELECT E.ENTRY_ID, E.DATE, E.ENGINEER, E.PROJECT, ', #sql, '
FROM entries as e');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Here is the SqlFiddle
Try
SELECT * FROM entries as e
INNER JOIN entries_allowanes_map as a ON e.entry_id=e.entry_id
INNER JOIN allownces as al ON al.allownce_id=a.allowance_id
I have two tables Triples and Tags
Triples Table has the following Columns
id PostID TagID Value
1 1 1 Murder
2 1 2 New Brunswick
3 2 1 Theft
4 2 3 Gun
Tags Table has the following Columns
id TagName
1 Incident
2 Location
3 Weapon
I am trying to write sql to create a Pivot Table with Dynamic Headers
Output should be like this
PostID Incident Location Weapon
1 Murder New Brunswick
2 Theft Gun
Any help in writing the SQL would be appreciated. I have seen examples online but could not figure out this one
In order to pivot the data in MySQL, you will need to use both an aggregate function and a CASE expression.
If you have a known number of columns, then you can hard-code the query:
select p.postid,
max(case when t.tagname = 'Incident' then p.value end) Incident,
max(case when t.tagname = 'Location' then p.value end) Location,
max(case when t.tagname = 'Weapon' then p.value end) Weapon
from triples p
left join tags t
on p.tagid = t.id
group by p.postid;
See SQL Fiddle with Demo
But if you have an unknown number of columns, then you will need to use a prepared statement to generate dynamic SQL:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(CASE WHEN TagName = ''',
TagName,
''' THEN p.value END) AS `',
TagName, '`'
)
) INTO #sql
FROM tags;
SET #sql
= CONCAT('SELECT p.postid, ', #sql, '
from triples p
left join tags t
on p.tagid = t.id
group by p.postid');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo.
Both will give the result:
| POSTID | INCIDENT | LOCATION | WEAPON |
----------------------------------------------
| 1 | Murder | New Brunswick | (null) |
| 2 | Theft | (null) | Gun |
I have a situation where I need to create columns depending on their content.
For instance, here is SQLFiddle - http://sqlfiddle.com/#!2/0ec7a/1.
I need to get a result like this:
--------------------------
| CITY | MALES | FEMALES |
--------------------------
| NY | 5 | 2 |
--------------------------
| DC | 2 | 1 |
--------------------------
How do I go about this?
I'm looking at CASE WHEN statements and IF statements from the MySQL Manual, but a clearer explanation would be very useful.
You don't even need CASE!
SELECT
city,
sum(gender = 'm') as males,
sum(gender = 'f') as females
FROM Population
group by city
See this working in SQLFiddle.
The reason this works is that in mysql, true is 1 and false is 0, so summing a condition counts how many times it was true!
For (most?) other databases, you must use the boring case inside the sum: sum(case when gender = 'm' then 1 else 0 end) etc
This type of data layout is called a "pivot". Some databases, like Oracle, support it natively through specific extensions to its flavour of SQL, but in mysql you have to "roll your own".
SELECT CITY,
SUM(CASE WHEN GENDER = 'M' THEN 1 ELSE 0 END) MALE,
SUM(CASE WHEN GENDER = 'F' THEN 1 ELSE 0 END) FEMALE
FROM Population
GROUP BY City
SQLFiddle Demo
You can also do prepared statement
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'SUM(CASE WHEN GENDER = ''',
GENDER,
''' then 1 ELSE 0 end) AS ',
GENDER
)
) INTO #sql
FROM Population;
SET #sql = CONCAT('SELECT CITY, ', #sql, '
FROM Population
GROUP BY City');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SQLFiddle Demo