Dynamically creating Columns from rows Access 2010 - sql

I am relatively new to Access and I have a table that has AuthorName, BookTitle, and CoAuthor. AuthorName and BookTitle are a composite key.
Currently the query pulls information like:
AuthorName---------BookTitle------CoAuthor
Steven King--------Dark Half------Peter Straub
Steven King--------Dark Half------John Doe
James Patterson----Another Time
Jeff Hanson--------Tales of Time---Joe Smith
I would like it to read (dynamically) if possible
AuthorName---------BookTitle---------CoAuthor1--------CoAuthor2
Steven King----------Dark Half--------Peter Straub-----Joe Doe
James Patterson----Another Time
Jeff Hanson----------Tales of Time----Joe Smith
So if there is another author that is later added, a third column for CoAuthor would appear.
Is this possible with VBA or SQL?

What you are asking goes against the whole point of using a relational database; in short, you should design your tables in a way that minimizes (rather eliminates) the need to redesign the tables. I suggest you read about database normalization.
Your question is, as a matter of fact, an example that I use very frequently when I teach about databases: How would you design a database to hold all the information about books and authors. The scenarios are:
A book can have one or more authors
An author may have written one or more books
So, this is a many-to-many relation, and there's a way to design such a database.
First, design a table for the authors:
tbl_authors
author_idd (primary key, numeric, preferibly autonumeric)
first_name (String)
last_name (String)
Then, design a table for the books:
tbl_books
book_id (primary key, numeric, preferibly autonumeric)
book_title (String)
And finally, a third table is needed to relate authors and books:
tbl_books_authors
book_id (primary key, numeric)
author_id (primary key, numeric)
main_author (boolean: "Yes" if it is the main author, "No" otherwise)
(Both fields must be part of the primary key)
And now, the main question: How to query for books and its authors?
Asuming the above design, you could write an SQL query to get the full list of books and its authors:
select book_title, first_name, last_name
from
tbl_authors as a
inner join tbl_books_authors as ba on a.authorId = ba.authorId
inner join tbl_books as b on ba.bookId = b.bookId
This way, you'll have something like this:
book_title | first_name | last_name
-----------+------------+-----------
book1 | John | Doe
book1 | Jane | Doe
book2 | John | Doe
book2 | Peter | Who
book3 | Jane | Doe
book4 | Peter | Who
book5 | John | Doe
book5 | Jane | Doe
book5 | Peter | Who
book5 | Jack | Black
Why is this design better than your original idea?
Because you won't need to alter the structure of your tables to add another author
Because you don't know a priori how many authors a book can have
Because you avoid redundancy in your database
Because, with this design, you'll be able to use front-end tools (like Access forms and reports, or other tools) to create almost any arraingment from this data.
Further reading:
The Access Web (specially "The Ten Commandments of Access")
Minor update
This kind of design will help you avoid lots and lots of headaches in the future, because you won't need to alter your tables every time you add a third or fourth author. I learned about database normalization some years ago reading "Running Access 97" by Jon Viescas (this is not a commercial, it's just a reference ;) )... an old book, yes, but it has a very good introduction on the topic.
Now, after you have normalized your database, you can use pivot queries to get what you need (as noted in the answer posted by Conrad Frix).

If your table had type like below
+-----------------+---------------+--------------+-----------+
| AuthorName | BookTitle | CoAuthor | Type |
+-----------------+---------------+--------------+-----------+
| Steven King | Dark Half | Peter Straub | CoAuthor1 |
| Steven King | Dark Half | John Doe | CoAuthor2 |
| James Patterson | Another Time | | CoAuthor1 |
| Jeff Hanson | Tales of Time | Joe Smith | CoAuthor1 |
+-----------------+---------------+--------------+-----------+
it would be a pretty simple transform
TRANSFORM First(Books.CoAuthor) AS FirstOfCoAuthor
SELECT Books.AuthorName, Books.BookTitle
FROM Books
GROUP BY Books.AuthorName, Books.BookTitle
PIVOT Books.Type;
Since it doesn't we need to create it on the fly by first assigning a number to each row simulating ROW_NUMBER OVER and then transforming. On large data sets this may be quite slow
TRANSFORM First(b.coauthor) AS firstofcoauthor
SELECT b.authorname,
b.booktitle
FROM (SELECT authorname,
booktitle,
coauthor,
'CoAuthor' & k AS Type
FROM (SELECT b.authorname,
b.booktitle,
b.coauthor,
Count(*) AS K
FROM books AS b
LEFT JOIN books AS b1
ON b.authorname = b1.authorname
WHERE [b].[coauthor] <= [b1].[coauthor]
OR (( ( b1.coauthor ) IS NULL ))
GROUP BY b.authorname,
b.booktitle,
b.coauthor) AS t) AS b
GROUP BY b.authorname,
b.booktitle
PIVOT b.type

Related

Auto generate columns in Microsoft Access table

How can we auto generate column/fields in microsoft access table ?
Scenario......
I have a table with personal details of my employee (EmployDetails)
I wants to put their everyday attendance in an another table.
Rather using separate records for everyday, I want to use a single record for an employ..
Eg : I wants to create a table with fields like below
EmployID, 01Jan2020, 02Jan2020, 03Jan2020,.........25May2020 and so on.......
It means everyday I have to generate a column automatically...
Can anybody help me ?
Generally you would define columns manually (whether that is through a UI or SQL).
With the information given I think the proper solution is to have two tables.
You have your "EmployDetails" which you would put their general info (name, contact information etc), and the key which would be the employee ID (unique, can be autogenerated or manual, just needs to be unique)
You would have a second table with a foreign key to the empployee ID in "EmployDetails" with a column called Date, and another called details (or whatever you are trying to capture in your date column idea).
Then you simply add rows for each day. Then you do a join query between the tables to look up all the "days" for an employee. This is called normalisation and how relational databases (such as Access) are designed to be used.
Employee Table:
EmpID | NAME | CONTACT
----------------------
1 | Jim | 222-2222
2 | Jan | 555-5555
Detail table:
DetailID | EmpID (foreign key) | Date | Hours_worked | Notes
-------------------------------------------------------------
10231 | 1 | 01Jan2020| 5 | Lazy Jim took off early
10233 | 2 | 02Jan2020| 8 | Jan is a hard worker
10240 | 1 | 02Jan2020| 7.5 | Finally he stays a full day
To find what Jim worked you do a join:
SELECT Employee.EmpID, Employee.Name, Details.Date, Details.Hours_worked, Details.Notes
FROM Employee
JOIN Details ON Employee.EmpID=Details.EmpID;
Of course this will give you a normalised result (which is generally what's wanted so you can iterate over it):
EmpID | NAME | Date | Hours_worked | Notes
-----------------------------------------------
1 | Jim | 01Jan2020 | 5 | ......
1 | Jim | 02Jan2020 | 7 | .......
If you want the results denormalised you'll have to look into pivot tables.
See more on creating foreign keys

Migrating legacy table to normalized data structure with foreign keys in Oracle SQL

I am having some trouble wrapping my head around remaking databases. I have a book database that includes only one table, where all of the authors data is included after each book. I'm trying to remake this database in order to have an author table and a book table.
I made the author table using :
CREATE TABLE AUTHORS
AS SELECT AUTHOR_NAME, AUTHOR_SURNAME, AUTHOR_BIRTHDATE
If I now want to remake the book table, how do I add the foreign key so that the author of each book will be the correct one? That is, if the first entry on the original book table was:
ISBN1 Title1 Author_Name1 Author_Surname1 Author_Birthdate1
How do I import this data into the new table so that the new author field, a foreign key, references the correct entry in the author table? Sorry if it's confusing.
You are looking to split the existing table into two tables, one to store the authors and the other for books. For this to work properly, you need to create a unique id for each author. Here is a step by step approach.
Assuming the following legacy data structure:
create table old_books (
isbn NUMBER(13, 0),
title VARCHAR2(200),
author_name VARCHAR2(200),
author_surname VARCHAR2(200),
author_birthdate DATE
);
And this sample data:
ISBN | TITLE | AUTHOR_NAME | AUTHOR_SURNAME | AUTHOR_BIRTHDATE
------------: | :----- | :---------- | :------------- | :---------------
1000000000001 | book 1 | name 1 | surname 1 | 01-MAR-90
1000000000002 | book 2 | name 2 | surname 2 | 01-MAR-95
1000000000003 | book 3 | name 1 | surname 1 | 01-MAR-90
First, let's create and feed the new data structure for authors (note that you don't want to use CREATE TABLE AS SELECT ... because this does not let you add constraints or other useful options).
To generate a unique author id, we use the IDENTITY feature (available starting Oracle 12c - without this feature, we would need to create a sequence and a trigger).
In legacy data, we assume that each author is uniquely identified by its name, surname and birthdate:
CREATE TABLE authors (
id NUMBER GENERATED ALWAYS AS IDENTITY,
name VARCHAR2(200),
surname VARCHAR2(200),
birthdate DATE,
PRIMARY KEY (id)
);
INSERT INTO AUTHORS (name, surname, birthdate)
SELECT DISTINCT author_name, author_surname, author_birthdate FROM old_books;
2 rows affected
SELECT * FROM authors;
ID | NAME | SURNAME | BIRTHDATE
-: | :----- | :-------- | :--------
1 | name 1 | surname 1 | 01-MAR-90
2 | name 2 | surname 2 | 01-MAR-95
With this first table in place, we can now create the books table. It contains a foreign key that references the primary key of the authors table. To feed the table, we need to join the legacy table with the new authors table to recover the id of each author:
CREATE TABLE books (
isbn NUMBER(13, 0),
title VARCHAR2(200),
author_id NUMBER,
CONSTRAINT book_author FOREIGN KEY(author_id) REFERENCES authors(id),
PRIMARY KEY (isbn)
);
INSERT INTO books(isbn, title, author_id)
SELECT ob.isbn, ob.title, a.id
FROM old_books ob
INNER JOIN authors a
ON a.name = ob.author_name
AND a.surname = ob.author_surname
AND a.birthdate = ob.author_birthdate;
3 rows affected
SELECT * FROM books;
ISBN | TITLE | AUTHOR_ID
------------: | :----- | --------:
1000000000001 | book 1 | 1
1000000000002 | book 2 | 2
1000000000003 | book 3 | 1
All set! Data is properly spread between the two tables, with the proper constraints in place. We can join both tables with a query like:
SELECT b.isbn, b.title, a.name, a.surname, a.birthdate
FROM authors a
INNER JOIN books b ON a.id = b.author_id;
ISBN | TITLE | NAME | SURNAME | BIRTHDATE
------------: | :----- | :----- | :-------- | :--------
1000000000001 | book 1 | name 1 | surname 1 | 01-MAR-90
1000000000002 | book 2 | name 2 | surname 2 | 01-MAR-95
1000000000003 | book 3 | name 1 | surname 1 | 01-MAR-90
You say that an author's first name plus surname are your author table's primary key. This is a valid approach. In case of two authors with the same name you'd have to find a solution like 'John' + 'Smith' and 'John R.' + 'Smith' or 'John' + 'Smith (the fantasy author)'. This is called a natural composite key, albeit not a perfect one as we may have to deal with duplicate names as mentioned. On the other hand there exist authors with the same name, so we may face this problem right away ;-)
Books are identified by their ISBN, which makes for an even better natural key, because there can be no duplicates. (Only if you wanted to add very old books or self-marketed books that have no ISBN, you'd have to create a fake ISBN.)
In order to have your book referring to an author, you must include the whole key, which is first and surname here. This is no redundancy, as this is the key needed to identify an author in your database.
CREATE TABLE books AS SELECT isbn, title, author_name, author_surname FROM old_table;
ALTER TABLE books ADD CONSTRAINT fk_book_author FOREIGN KEY (author_name, author_surname)
REFERENCES authors (author_name, author_surname);
An alternative would be to introduce surrogate (i.e. technical) keys. You would generate an ID (a number) for each book and each author and work with them. (That means the book table would contain an author_id.) But for a good database you should still think about what identifies a row naturally. This makes it easier for people who write the queries later. (E.g. someone asks to select a list of authors and the number of books they've written. How to write that query? Does it suffice to show first and surname or could we end up with two rows "John Smith | 5" and "John Smith | 2" and the enquirer saying they cannot use this ambiguous result?) Even when providing surrogate keys you should still have a unique constraint on the natural key, if there is one. For books with optional ISBNs this may be title + author_id and for authors it could be first name + surname + date of birth.
By the way: There exist books with more than one author ;-)

SQL comma separated column loop

I have a serious problem with SQL that already took me 3 hours. I have two tables like these:
First table: Employees
ID | NAME
---+--------
1 | John
2 | Mike
3 | Robert
Second table: Customers
ID | NAME | EMPLOYEES
---+---------+--------------
1 | Michael | 2,3
2 | Julia | 1
3 | Mila | 1,2,3
I want the output like this:
Michael | Mike, Robert
Julia | John
Mila | John, Mike, Robert
What should the SQL command to get the expected output?
Select A.Name
,Employees = (Select Stuff((Select Distinct ',' +Name From Employees Where charindex(','+cast(ID as varchar(25))+',',','+A.EMPLOYEE_ID+',')>0 For XML Path ('')),1,1,'') )
From Customers A
Returns
Name Employees
Michael Mike,Robert
Julia John
Mila John,Mike,Robert
This is an awful data structure and you should fix it. That is the primary thing. Storing numbers as strings is bad. Storing multiple values in a column is bad. Not declaring foreign key relationships is bad.
That said, what can you do if someone else set up such a database and did so in this bad way? Well, you can do:
select c.*, e.name
from customers c join
employees e
on ',' + cast(e.id as varchar(255)) + ',' like '%,' + c.employee_id + ',%';
Note that this query cannot be optimized using normal SQL methods, such as indexes, because the JOIN condition is too complicated.
This will give you more rows than you have asked for:
Michael Mike
Michael Robert
Julia John
Mila John
Mila Mike
Mila Robert
However, this is the normal way that SQL works, so you should get used to it.

Create a new table with each new record

I am building a Hospital Management System and I wanted to make a table in the database that contains all the hospitals. Each hospital has another table that contains info about its employees.
If I wanted to add a new record (a new Hospital) in the hospital table after the system is released, can the system generate a new table for that new hospital's employees?
It would have a standard form and the system would ask the user to fill it (through GUI, or any other way).
Is it technically possible? And if not is there other ways to do it?
Why would you need to create a seperate table for each hospital? Add Hospital_ID as a column to the Employee table and then you can tell, from that one table, what hospital an mployee works for.
SQLFiddle
First you should read about Table relationships. It's really important that you understand how it works before desgnining your database.
Now as for your question, tables don't contain tables. This is why you should review you design.
You should have a those tables :
Hospital
---------------------------------------------------
| ID | Name | Address | Phone |
---------------------------------------------------
| 1 | SomeName | 13 Mercy Street | 555-555-5555 |
---------------------------------------------------
Employee
-------------------------
| ID | Hospital_ID | Name |
-------------------------
| 01 | 1 | John |
-------------------------
This way every employee is associated with an hospital. We now know John works in the SomeName hospital.
To help in your research,
Hospital.ID is a PRIMARY KEY
Employee.ID is a PRIMARY KEY
Employee.Hospital_ID is a FOREIGN KEY

In mysql, what is an efficient way to get only rows in a table by a distinct value in one of that table's columns?

I'm relatively new to mysql so bear with me.
I have a table that looks a bit like this:
ID | Name | Location
0 | John | Los Angeles
1 | Joe | San Jose
2 | Jane | New York
3 | Sal | Boise
4 | Jay | New York
5 | Kate | San Jose
I need a SELECT statement that gets all rows, with the exception that if Location is repeated, that row is ignored. The result should look something like this:
0 | John | Los Angeles
1 | Joe | San Jose
2 | Jane | New York
3 | Sal | Boise
The important thing is that my table is very, very large, with hundreds of thousands of rows. Most things I've tried as ended up with select statements that take literally 30+ minutes to complete!
This is how I would do it:
SELECT mt1.ID, mt1.Name, mt1.Location
FROM mytable mt1
JOIN (SELECT MIN(id) AS ID
FROM Mytable
GROUP BY location) mt2
ON mt1.id = mt2.Id
The derived table get the minimum id for each location. Then joins to the table to pull the rest of the data.
Incidentally ID is a horrible choice for naming the id field. Please think about using tablenameID instead. It is helpful for reporting not to have the same name for the id fields in differnt tables and it makes if FAR less likely that you will make an accidental join mistake and join tothe ID in the wrong table. It also makes the PK/FK relationships easier to see in my opinion.
SELECT ID, Name, Location
FROM table
GROUP BY Location
Do you have an index on Location? That should help improve the speed a lot.
you can use
SELECT * FROM tbl GROUP BY Location
creating an index on Location can dramatically help reduce the querying time.
Also if there are more number of columns in your table, specifying only the required columns instead of using * will improve performance further.