How to create an output under Oracle SQL Developer? - sql-server-2008

I have two table say with table name "Books" , "Params" as
Input tables are attached
I need an output as shown in the below figure:
I need an output as in below format

you can pivot the params table using aggregation and left join it with the books table like this:
select b.*, p.author, p.issue_date
from books b
left join (
select id,
max(case when Attr_id = 'Author' then Others end) as author,
max(case when Attr_id = 'Issue Date' then Others end) as issue_date
from params
group by id
) p on b.id = p.id
order by id;
Produces:
id name author issue_date
101 Sumit Bhagat
102 Ben Sinha 2005
103 Amit

Related

Merging MySQL database rows into columns

I have a database with the structure, where id and name are the key
id
name
cp
time
1
abc
1
10
1
abc
2
3
1
abc
3
12
2
xyx
1
12
2
xyx
2
11
2
xyx
2
13
and I need a query to merge it into a new table structure where its ID and name are only 1 row with the following structure, with time in each column value.
id
name
cp1
cp2
cp3
1
abc
10
3
12
2
xyz
12
11
13
Any help is appreciated thank you.
Assuming that you have a typo in your data and the 6th record should have a cp of 3, then you can use conditional aggregation:
SELECT t.id,
t.name,
MAX(CASE WHEN cp = 1 THEN t.time END) AS cp1,
MAX(CASE WHEN cp = 2 THEN t.time END) AS cp2,
MAX(CASE WHEN cp = 3 THEN t.time END) AS cp3
FROM T AS t
GROUP BY t.id, t.name;
If you are guaranteed to have one record per combination of id, name and cp then the MAX is largely irrelevant because you are taking the MAX of just one row, so it is deterministic. If you could have duplicates then you may need additional logic to determine which of the multiple records should be returned, or if you want to apply different aggregation (e.g. SUM).
Example on DB Fiddle
You can join your table 3 times:
SELECT m.id as id,
m.name as name,
m1.time as cp1,
m2.time as cp2,
m3.time as cp3
FROM mytable m -- This is the base table
LEFT JOIN mytable m1 ON -- This is a join to take cp1 if present
m.id = m1.id
AND m.name = m1.name
AND m1.cp = 1
LEFT JOIN mytable m2 ON -- This is a join to take cp2 if present
m.id = m2.id
AND m.name = m2.name
AND m2.cp = 2
LEFT JOIN mytable m3 ON -- This is a join to take cp3 if present
m.id = m3.id
AND m.name = m3.name
AND m3.cp = 3
GROUP BY m.id,
m.name,
m1.time,
m2.time,
m3.time
It has been joined 3 times to be sure that if you have any of cp1, cp2 or cp3 null it works. If you are sure that there are no rows absence for cp1 cp2 cp3 you can leave just 2 inner joins (instead of 3 left joins).
Note that this solution works with any relational database, not only mysql because there is no reference to special function of the database, but only standard SQL joins.
GROUP BY will summarize each id and name. And GROUP_CONCAT will give you the list of cp's. Here's a sample query.
SELECT id, name, GROUP_CONCAT(cp) as cps
FROM your_table
GROUP BY id, name

Is this join possible in SQL?

I am not sure how else to ask the question, so I will give an example, to see if this is possible with SQL.
Let's say a customer visits a store, and a System creates a VisitID. Then he places an Order in a table of Orders, linked to the VisitID. Then he fills in a Shipping Form, in a table of ShippingForms, linked to the Visit ID. Then the system generates a Receipt, in a table of ReceiptForms, linked to the VisitID. Then he fills in a Return Form, in a table of ReturnForms, linked to the VisitID.
So, is there a way to query the system to show for a VisitID there is/isn't an Orders record, Shipping Form record, Receipt record, Return record? This would be handy in a DBGrid to show all the activities of the customer on that VisitID. Each table (Orders, ShippingForm, ReceiptForm, ReturnForm, etc.) is of a different structure and different fields, but linked by the Visit ID, and may be present or not present, or may have several Orders during that VisitID.
So -- Select Orders, ShippingForm, ReceiptForm, ReturnForm where VisitID=x.
so I could present the information in a grid such as:
{
VisitID 2315
OrderID 1256
OrderID 1257
OrderID 1258
ReceiptID 5124
ReceiptID 5125
ReceiptID 5126
ShippingID 99023
ReturnID 582812
}
Visits that do not have records in all tables:
Select *
from Visits v -- visit always exists
left join Orders o on o.visitId = v.visitId
left join ShippingForm s on s.visitId = v.visitId
left join ReceiptForm r on r.visitId = v.visitId
left join ReturnForm rf on fr.visitId - v.visitId
where v.VisitID=x
and (o.visitId is null or s.visitId is null or r.visitId is null or fr.visitId is null ) -- it is null when record does not exist
Maybe you are looking for UNION ALL:
select visitid, type, id
from
(
select visitid, 'visit' as type, 1 as sortkey, null as id from visits
union all
select visitid, 'order' as type, 2 as sortkey, orderid as id from orders
union all
select visitid, 'receipt' as type, 3 as sortkey, receiptid as id from receipts
union all
select visitid, 'shipping' as type, 4 as sortkey, shippingid as id from shippings
union all
select visitid, 'return' as type, 5 as sortkey, returnid as id from returns
) data
order by visitid, sortkey;
Or you may want string aggregation. This is DBMS dependent and you haven't mentioned your DBMS. This is for MySQL:
select
visitid,
ord.ids as orderids,
rcp.ids as receiptids,
shp.ids as shippingids,
ret.ids as returnids
from visits v
left join
(select visitid, group_concat(orderid) as ids from orders group by visitid) ord
using (visitid)
left join
(select visitid, group_concat(receiptid) as ids from receipts group by visitid) rcp
using (visitid)
left join
(select visitid, group_concat(shippingid) as ids from shippings group by visitid) shp
using (visitid)
left join
(select visitid, group_concat(returnid) as ids from returns group by visitid) ret
using (visitid)
order by visitid;
Thank you all, you have allowed me to answer the question. For my purposes, I combined the results to make this sol
select
OrderID as FormID,
1 as FormType,
'Order ID' as FormTitle
from Orders O, Visits V where (O.VisitID=V.VisitID) and (V.VisitID=112)
union all
ShippingID as FormID,
2 as FormType,
'Shipping ID' as FormTitle
from Shipping S, Visits V where (S.VisitID=V.VisitID) and (V.VisitID=112)
etc.
so now have a line by line table for VisitID=112:
FormID FormType FormTitle
23 1 Order ID
26 1 Order ID
28 1 Order ID
342 2 Shipping ID
343 2 Shipping ID
367 2 Shipping ID

How can I get data from a table that fulfill two or more conditions on meta data table?

I know this is similar to this question but there is still no working answer yet and I will try to explain better.
So I have THREE tables, which are member, meta_name, and meta_value. I think you already know how they are related to each other. For example, assume I have these rows:
member table:
memberID | name
1 | john
meta_name table:
meta_nameID | name
1 | address
2 | jobTitle
meta_value table:
meta_valueID | meta_nameID | memberID | value
1 | 1 | 1 | California
2 | 2 | 1 | Manager
So John has two meta data which are address and jobTitle. The meta data are stored in meta_value table, and the mete_value table has identifiers in meta_name table. It's just a basic meta data system.
Now the question is, how can I get members that fulfill two or more conditions on the meta_value table? Something like, "get members that have an address at California AND a jobTitle as Manager"?
I have tried this query:
SELECT * FROM member JOIN meta_value ON member.memberID = meta_value.memberID WHERE (meta_nameID = '1' AND value = '3') AND (meta_nameID = '2' AND value = 'Jonggol')
I know that's an ugly-not working query but I hope that will help you understand what I'm going to achieve. Thanks!
NOTE: I actually don't need the meta_value table data. I just want to get members that fulfill the conditions.
There are a number of different options.
Straightforward:
SELECT
*
FROM
member
WHERE
EXISTS (
SELECT
*
FROM
meta_value
WHERE
meta_value.memberID = member.memberID
AND meta_value.meta_nameID = 1
AND meta_value.value = '3'
)
AND EXISTS (
SELECT
*
FROM
meta_value
WHERE
meta_value.memberID = member.memberID
AND meta_value.meta_nameID = 2
AND meta_value.value = 'Jonggol'
)
SELECT
*
FROM
member
WHERE
memberID IN (
SELECT
memberID
FROM
meta_value
WHERE
meta_value.meta_nameID = 1
AND meta_value.value = '3'
)
AND memberID IN (
SELECT
memberID
FROM
meta_value
WHERE
meta_value.meta_nameID = 2
AND meta_value.value = 'Jonggol'
)
Another way:
SELECT
member.*
, SUM(IF((meta_value.meta_nameID = 1 AND meta_value.value = '3') OR (meta_value.meta_nameID = 2 AND meta_value.value = 'Jonggol'), 1, 0)) AS x
FROM
member
INNER JOIN meta_value ON (
meta_value.memberID = member.memberID
)
GROUP BY
member.memberID
HAVING
x = 2
However, I'd like to note that such DB schema should only be used to store data that require filtering.
You need to use sub queries. Try the following query to select members with a particular address and a job title.
SELECT member.name
FROM member
WHERE
memberID IN
(SELECT DISTINCT memberID
FROM meta_value
WHERE meta_nameID IN
(SELECT DISTINCT meta_nameID FROM meta_name WHERE name='address')
AND value='California')
AND memberID IN
(SELECT DISTINCT memberID
FROM meta_value
WHERE meta_nameID IN
(SELECT DISTINCT meta_nameID FROM meta_name WHERE name='jobTitle')
AND value='Manager')
You also try a smaller query using with clause:
(Here we are creating a tmp table which has both address and job title, later join the address separately to get just address and jobTitle to get just job title. This will give you a member level table with address and jobTitle as columns for easy use in any subsequent queries)
WITH tmp AS
(SELECT * FROM meta_value mv INNER JOIN meta_name mn ON mv.meta_nameID=mn.meta_nameID)
SELECT member.name , add.value , job.value
FROM member
LEFT JOIN (SELECT * FROM tmp WHERE name='address') add ON member.memberID = add.memberID
LEFT JOIN (SELECT * FROM tmp WHERE name='jobTitle') job ON member.memberID = job.memberID
WHERE add.value = 'required address' AND add.job.value ='required job title'

Concat foreign key values from a self related table

I have a products database which has a multi-tier category structure. Products are assigned to a category. The category table looks like this:
id name parent_id
================================
1 Electronics NULL
2 AV 1
3 Speakers 2
4 Wireless 3
What I want to do is, as part of my SELECT statement for products, output a concatenated string of the category tree.
The product is always assigned to the last category, so for example, Product "500w Wireless Speakers" would be assigned to category_id 4 (based on the above).
The ouputted column should be Electronics-AV-Speakers-Wireless.
Is this possible to do? I have looked at GROUP_CONCAT() but I'm having trouble working out the correct syntax.
Join as many times as you need, and concat the names:
select concat(a.name, '-', b.name, '-', c.name, '-', d.name) name
from mytable a
join mytable b on a.id = b.parent_id
join mytable c on b.id = c.parent_id
join mytable d on c.id = d.parent_id;

SQL Query: How to improve?

I have two tables in a MySQL database:
Book(title, publisher, year) title is primary key
Author(name, title) title is foreign key to Book
I am trying to select the name of the Authors that published a book each year from 2000 to 2005 inclusive. This SQL query works, but is there a way to do this that makes it easier to change the date range if needed?
SELECT DISTINCT name
FROM Author
WHERE name IN (SELECT Author.name
FROM Author INNER JOIN Book ON (Author.title = Book.title)
WHERE year = 2000)
AND name IN
(SELECT Author.name
FROM Author INNER JOIN Book ON (Author.title = Book.title)
WHERE year = 2001)
AND name IN
(SELECT Author.name
FROM Author INNER JOIN Book ON (Author.title = Book.title)
WHERE year = 2002)
AND name IN
(SELECT Author.name
FROM Author INNER JOIN Book ON (Author.title = Book.title)
WHERE year = 2003)
AND name IN
(SELECT Author.name
FROM Author INNER JOIN Book ON (Author.title = Book.title)
WHERE year = 2004)
AND name IN
(SELECT Author.name
FROM Author INNER JOIN Book ON (Author.title = Book.title)
WHERE year = 2005);
Here's two ways to do it, and how another approach is wrong due to a subtle fault.
SQL Fiddle
MySQL 5.5.32 Schema Setup:
create table Book (title varchar(10), year int) ;
create table Author (name varchar(10), title varchar(10));
insert Book values
('Book1',2000),('Book2',2000),
('Book3',2000),('Book4',2000),
('Book5',2000),('Book6',2000),
('Book7',2001),('Book8',2002),
('Book9',2003),('Book10',2004),
('Book11',2005);
insert into Author values
('Author1','Book1'),('Author1','Book2'),
('Author1','Book3'),('Author1','Book4'),
('Author1','Book5'),('Author1','Book6'),
('Author2','Book6'),('Author2','Book7'),
('Author2','Book8'),('Author2','Book9'),
('Author2','Book10'),('Author2','Book11');
# author1 has written 6 books in one year
# author2 has written 1 book in every of the six years
Query 1:
# incorrect as it matches author1 who has 6 books in a single year
SELECT name from Author
INNER JOIN BOOK on Author.title = Book.Title
WHERE year IN (2000,2001,2002,2003,2004,2005)
GROUP BY name
HAVING COUNT(name) = 6
Results:
| NAME |
|---------|
| Author1 |
| Author2 |
Query 2:
# correct as it counts distinct years
SELECT name from Author
INNER JOIN BOOK on Author.title = Book.Title
WHERE year IN (2000,2001,2002,2003,2004,2005)
GROUP BY name
HAVING COUNT(DISTINCT year) = 6
Results:
| NAME |
|---------|
| Author2 |
Query 3:
# correct using relational division
SELECT DISTINCT name
FROM Author A1
INNER JOIN Book B1 ON A1.title = B1.Title
WHERE NOT EXISTS (
SELECT *
FROM Book B2
WHERE year IN (2000,2001,2002,2003,2004,2005)
AND NOT EXISTS (
SELECT *
FROM Author A2
INNER JOIN Book B3 ON A2.title = B3.Title
WHERE (A1.name = A2.name)
AND (B3.year = B2.year)
)
)
Results:
| NAME |
|---------|
| Author2 |
I would put an 'OR' clause in the where statement. It depends on how your table is set up but it should work:
SELECT DISTINCT name
FROM Author
WHERE name IN (SELECT Author.name
FROM Author INNER JOIN Book ON (Author.title = Book.title)
WHERE year = 2000)
Something like this, I didn't test it but you can group by and select only the ones that would have 6 rows:
SELECT a.name FROM Author a
INNER JOIN Book b
ON a.title = b.title
WHERE b.year BETWEEN 2000 AND 2005
GROUP BY name HAVING COUNT(a.name) = 6
I would use arguments
declare #StartYear int = 2000
declare #EndYear int = 2005
select a.name
from author a
inner join book b on a.title = b.title
where year between #StartDate and #EndDate
group by a.name
having count(distinct year) = #EndDate- #StartDate
It's easy to modify and modify itself whenever year you input