SQL - Query if the same card exists among multiple decks - sql

Given the schema below, how can I query if and how many of the same character card is in multiple decks, without changing the underlying data?
CREATE TABLE 'deck' (
'deckId' BIGINT(20) NOT NULL AUTO_INCREMENT,
'title' TEXT(255) NOT NULL,
PRIMARY KEY ('deckId')
);
CREATE TABLE 'character' (
'characterId' BIGINT(20) NOT NULL AUTO_INCREMENT,
'name' VARCHAR(255) NOT NULL,
PRIMARY KEY ('userId')
);
CREATE TABLE 'card' (
'cardId' BIGINT(20) NOT NULL AUTO_INCREMENT,
'color' VARCHAR(255) NOT NULL,
'characterId' BIGINT(20) NOT NULL,
'deckId' BIGINT(20) NOT NULL,
PRIMARY KEY ('cardId'),
CONSTRAINT 'FK_card_character'
FOREIGN KEY ('characterId')
REFERENCES 'character' ('characterId'),
CONSTRAINT 'FK_card_deck'
FOREIGN KEY ('deckId')
REFERENCES 'deck' ('deckId')
);

The good news is this is a properly normalized schema. So often the questions posed here involve bad database design.
Essentially you need to GROUP BY, and then use the HAVING clause. The JOIN here is just cosmetic so you can get the Character name, but usually that is something people want. When you are getting comfortable with this type of query you might want to start with just the underlying table, which in your case is card.
SELECT ch.characterId, ch.name, count(*) as countOf
FROM card ca
JOIN character ch ON ch.characterId = ca.characterId
GROUP BY ca.characterId
HAVING COUNT(*) > 1

Related

beginner sql missing keyword and invalid identifier

CREATE table Book
(
book_title varchar (100) not null ,
book_genre char(60) not null,
Date_of_publish date not null,
user_code char(7) not null ,
book_id char (7) primary key not null ,
constraint writer__id_fk foreign key (writer_id),
constraint publisher__id_fk foreign key (publisher_id)
);
I'm getting
[ORA-00905: missing keyword]
in publisher table
CREATE table publisher
(
publisher_id char (7) primary key not null,
publisher_name char(20) not null,
publisher_number char(10) not null,
publisher_email varchar2(60) not null,
publisher_address varchar2(60) not null,
);
I'm getting
[ORA-00904: : invalid identifier]
The following SQL creates a FOREIGN KEY on the "PersonID" column when the "Orders" table is created:
CREATE TABLE Orders (
OrderID int NOT NULL,
OrderNumber int NOT NULL,
PersonID int,
PRIMARY KEY (OrderID),
FOREIGN KEY (PersonID) REFERENCES Persons(PersonID)
);
Refer this link for more details
https://www.w3schools.com/sql/sql_foreignkey.asp
Hope this helps.
Welcome to the wonderful world of SQL! :-)
General remark:
Please tell us what kind of DBMS you're using. MySQL? SQL Server? Oracle? SQlite? Different systems use different kinds of syntaxes.
First statement:
The problem seems to be in the FOREIGN KEY-portion.
Usually, you'll state something like:
CONSTRAINT [constraint_name] FOREIGN KEY([column_in_this_table]) REFERENCES OTHER_TABLE([column_in_other_table])
edit (added):
The [column_in_this_table] has to exist in your DDL (CREATE TABLE-statement), like so:
CREATE TABLE Book ( book_title ... etc., publisher_id INT, CONSTRAINT FK_publ_id FOREIGN KEY(publisher_id) REFERENCES publisher(publisher_id));
Here, you'll have a 'original' column called 'publisher_id', in the 'publisher'-table. You refer to it from within the 'Book'-table, by first having a 'publisher_id' column in the 'Book'-table (which should have the same DDL as the original column by the way). Next, you'll add a FOREIGN KEY to the 'Book'-table, that is imposed on the Book(publisher_id) column. Note, that you could also name the column in your 'Book'-table differently -- like, say, 'Spongebob' or 'Patrick'. But for future use, you'd like naming conventions that tell what you might expect to find in a column. So you'd name columns for what they contain.
Second statement:
The problem is with the last portion of your statement, where there's a comma after the NOT NULL portion for column publisher_address.
(Part of) your statement:
publisher_address varchar2(60) not null, );
Try replacing that with:
publisher_address VARCHAR2(60) NOT NULL);
edit (note to self):
VARCHAR2 turns out to be a valid datatype in Oracle databases (see:
Oracle documentation)
For your first table, the Foreign Keys do not reference any table. For your second table, I would imagine that comma after your last column isn't helping anything.
So this is the answer.
CREATE table Book
(
book_title varchar (100) not null ,
book_genre char(60) not null,
Date_of_publish date not null,
user_code char(7) not null ,
publisher_id char (7) not null,
writer_id char(7) not null,
book_id char (7) primary key not null ,
CONSTRAINT book_writer_id_fk FOREIGN KEY(writer_id) REFERENCES writer(writer_id),
CONSTRAINT book_publisher_id_fk FOREIGN KEY(publisher_id) REFERENCES publisher(publisher_id)
);
CREATE table publisher
(
publisher_id char (7) primary key not null,
publisher_name char(20) not null,
publisher_number char(10) not null,
publisher_email varchar2(60) not null,
publisher_address varchar2(60) not null
);

SQL Relationship Cardinality in Logical Design

I have the following issue. We are currently working on a system for a shuttle service company. Now, part of the entities in the database for this system include numerous lookup tables (such as vehicle_type, employee_status, etc), as well as some other tables, such as vehicle and vehicle_service log.
Now the issue we as a team are having is that we cannot agree on what the logical relationship cardinalities between entities should be. The two main problem relationships include the tables defined as follows:
CREATE TABLE IF NOT EXISTS `user_type` (
`type_id` tinyint(4) NOT NULL AUTO_INCREMENT,
`description` varchar(200) NOT NULL,
PRIMARY KEY (`type_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Store the user types - employee
or consultant' AUTO_INCREMENT=1 ;
which is linked to
CREATE TABLE IF NOT EXISTS `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(100) NOT NULL,
`password` varchar(100) NOT NULL,
`user_type` tinyint(4) NOT NULL,
PRIMARY KEY (`user_id`),
KEY `user_type` (`user_type`),
KEY `username` (`username`),
KEY `login` (`username`,`password`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Table used when logging in
to check access level, type of user, etc. ' AUTO_INCREMENT=1 ;
The user table includes other irrelevant data. So the issue here is that I say (because MySQL Workbench reverse engineered it that way and it makes more sense) that the relationship should be 1-many, while another team member says that it should be 0-many (because some records may exist in the user_type table that aren't used in the user table)
The other table relationship we are having words about are defined as follows:
CREATE TABLE IF NOT EXISTS `vehicle` (
`vehicle_id` int(11) NOT NULL AUTO_INCREMENT,
`registration_number` varchar(10) NOT NULL,
PRIMARY KEY (`vehicle_id`),
UNIQUE KEY `registration_number` (`registration_number`),
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Actual vehicle information'
AUTO_INCREMENT=1 ;
Again, with some other columns not relative to the question. This links with
CREATE TABLE IF NOT EXISTS `service_log` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`vehicle_id` int(11) NOT NULL,
`description` text NOT NULL,
`date` date NOT NULL,
`cost` double NOT NULL,
PRIMARY KEY (`id`),
KEY `vehicle_id` (`vehicle_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Store records of all services
to vehicles' AUTO_INCREMENT=1 ;
Should this be 1-many or 0-many because a vehicle may not yet go in for a service? According to me it should be 1-many, but I don't know if this works logically.
We are all very confused about this whole logical modelling thing, so any help would be much appreciated!
I figured it would be easier for me to create the DB first and then reverse engineer it to a physical model, but never though about logical.
Zero to many if it is optional. Say for example a Sales Rep would have a zero or many customer. Why is that? Because if there is a new sales rep then it would mean he/she has no customer to begin with unless of course he/she assume the accounts of a resigned Sales Rep.
On the other hand one or many is mandatory. For example a an Order which has order date and customer who ordered should have at least one record on Order Detail table. Let's say a customer ordered a tablet last 04/22/2013 then he/she would have:
Order table
----------------------------------------
Orderid. OrderDate. Customermnum
----------------------------------------
1. 04/22/2013 101
Order detail table
----------------------------------------
Orderid. Productid. Qty. quotedprice
----------------------------------------
1. T101 1 500
So, in your case User to UserType is 1 to 0 or many beacause a user type may have not been used by any user yet.
Now, vehicle to service It is also 1 to 0 or many since a vehicle may not necessarily have a service done yet.

I get "a foreign key constraint fails" error when attempting to join two classes/tables

I have these classes, abbreviated for practical reasons:
class CV {
Date dateCreated
static hasMany=[proposals: Proposal]
}
class Proposal {
String name
Date date_started
static hasMany = [CVs: CV]
static belongsTo = CV
}
Grails creates tables for both these classes, and a third class named "cv_proposals" joining them. So far, so good. I have data in both the CV and the Proposal tables, they both have autoincremented "id" values. All good.
in Oracle MySQL Workbench, I try to manually add values to the joining table to get some dummy data to work with. I get an error message with this trace:
ERROR 1452: Cannot add or update a child row: a foreign key constraint fails
(cvreg_utv.cv_proposals, CONSTRAINT FK17D946F55677A672 FOREIGN KEY (cv_id) REFERENCES cv (id))
I made sure both the tables had several lines of data in them, and that I could edit both of them separately.
After trying dropping and recreating the table, altering the classes back and forth, I'm kind of convinced that this operation somehow has to be done through a running Grails application. So I write this script in a controller and run it:
def g = CV.get(1)
Proposal proposal = g.addToProposals(new Proposal(
name: "SavingTest",
date_started: new Date())).save()
I still get the same error, though. Is this not the right way to define a proposal that is connected to a certain CV? Am I wrong in using a many-to-many connection here somehow?
Edit: adding the schema-create script for the joining table
delimiter $$
CREATE TABLE `cv_proposals` (
`proposal_id` bigint(20) NOT NULL,
`cv_id` bigint(20) NOT NULL,
PRIMARY KEY (`cv_id`,`proposal_id`),
KEY `FK17D946F55677A672` (`cv_id`),
KEY `FK17D946F5F7217832` (`proposal_id`),
CONSTRAINT `FK17D946F5F7217832` FOREIGN KEY (`proposal_id`) REFERENCES `proposal` (`id`),
CONSTRAINT `FK17D946F55677A672` FOREIGN KEY (`cv_id`) REFERENCES `cv` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1$$
And the CV table:
CREATE TABLE `cv` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`version` bigint(20) NOT NULL,
`user_id` bigint(20) NOT NULL,
`version_name` varchar(255) DEFAULT NULL,
`date_created` datetime NOT NULL,
`last_updated` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `FKC734A9AB992` (`user_id`)
) ENGINE=MyISAM AUTO_INCREMENT=101 DEFAULT CHARSET=latin1$$
And the Proposal table:
CREATE TABLE `proposal` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`version` bigint(20) NOT NULL,
`date_ended` datetime NOT NULL,
`date_started` datetime NOT NULL,
`description` varchar(500) DEFAULT NULL,
`name` varchar(500) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=latin1$$
This is the insert script I tried to run:
INSERT INTO `cvreg_utv`.`cv_proposals` (`proposal_id`, `cv_id`)
VALUES ('1', '1');
You crated the tables manually? It's interesting that cv table is using MyISAM engine and the others uses InnoDB.
I think you want to use InnoDB to all your tables, since this engine is transactional. In my test, I also was unable to create the cv_proposals table until I changed the cv creation:
CREATE TABLE cv (
id bigint(20) NOT NULL AUTO_INCREMENT,
version bigint(20) NOT NULL,
user_id bigint(20) NOT NULL,
version_name varchar(255) DEFAULT NULL,
date_created datetime NOT NULL,
last_updated datetime NOT NULL,
PRIMARY KEY (id),
KEY FKC734A9AB992 (user_id)
) ENGINE=InnoDB AUTO_INCREMENT=101
After that, the insert's worked smoothly.

Specify sorting criteria manually for a set of records of a many-to-many relationship

I am designing a many-to-many relationship between two models: Cell and Isolator:
CREATE TABLE `isolator` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(127) NOT NULL,
`surname` varchar(127) NOT NULL,
PRIMARY KEY (`id`),
);
CREATE TABLE `cell` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(127) NOT NULL,
`gen` varchar(127) NOT NULL,
PRIMARY KEY (`id`),
);
CREATE TABLE `cell_isolators` (
`cell_id` bigint(20) NOT NULL DEFAULT '0',
`isolator_id` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`cell_id`,`isolator_id`),
CONSTRAINT `cell_isolator_id` FOREIGN KEY (`isolator_id`) REFERENCES `isolator` (`id`),
CONSTRAINT `isolator_cell_id` FOREIGN KEY (`cell_id`) REFERENCES `cell` (`id`) ON DELETE CASCADE
);
The client has asked me the possibility of specifying the order on which the list of isolators for a cell is shown, since it's important for publication purposes.
What is the best approach to model that? I was thinking on adding a third field to the many-to-many relation (e.g. sort_order), but I would like to know if there are other alternatives.
Thanks!
The only way to do that in the general case is to add a column to "cell_isolators".
The data type is application-dependent to a certain extent. I've seen this done with columns as integers, floats or decimals, and alphanumerics (varchar(n)).
Populating and maintaining it can be troublesome, though. That doesn't have anything to do with the database design; it's just painful to maintain the sort order for more than a few rows, especially if there are regular inserts to the table and changes to the sort order. Fortunately, that job usually falls to the user interface, not to the dbms.

How can I optimize this MySQL query that involves two left joins?

I cannot figure out why my query slows down. What it boils down to are four tables: team, player, equipment, and metadata. Records in player and equipment have a FK to team, making team a parent of player and equipment. And all three of those tables' rows each have a record in metadata which stores things like creation date, creator user id, etc.
What I would like to retrieve all at once are any player and equipment records that belong to a particular team, in order of creation date. I start from the metadata table and left join the player and equipment tables via the metadata_id FK, but when I try to filter the SELECT to only retrieve records for a certain team, the query slow down big time when there are lots of rows.
Here is the query:
SELECT metadata.creation_date, player.id, equipment.id
FROM
metadata
JOIN datatype ON datatype.id = metadata.datatype_id
LEFT JOIN player ON player.metadata_id = metadata.id
LEFT JOIN equipment ON equipment.metadata_id = metadata.id
WHERE
datatype.name IN ('player', 'equipment')
AND (player.team_id = 1 OR equipment.team_id = 1)
ORDER BY metadata.creation_date;
You'll need to add a lot of rows to really see the slow down, around 10,000 for each table. What I don't understand is why it is really quick if I only filter in the where clause on one table, for example: "... AND player.team_id = 1" But when I add the other to make it "... AND (player.team_id = 1 OR equipment.team_id = 1)" it takes much, much longer.
Here are the tables and datatypes. Note that one thing that seems to help a lot, but not all that much, is the combined keys on player and equipment for metadata_id and team_id.
CREATE TABLE `metadata` (
`id` INT(4) unsigned NOT NULL auto_increment,
`creation_date` DATETIME NOT NULL,
`datatype_id` INT(4) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `datatype` (
`id` INT(4) unsigned NOT NULL auto_increment,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `team` (
`id` INT(4) unsigned NOT NULL auto_increment,
`metadata_id` INT(4) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `player` (
`id` INT(4) unsigned NOT NULL auto_increment,
`metadata_id` INT(4) unsigned NOT NULL,
`team_id` INT(4) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
CREATE TABLE `equipment` (
`id` INT(4) unsigned NOT NULL auto_increment,
`metadata_id` INT(4) unsigned NOT NULL,
`team_id` INT(4) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
ALTER TABLE `metadata` ADD INDEX ( `datatype_id` ),
ADD INDEX ( `creation_date` );
ALTER TABLE `team` ADD INDEX ( `metadata_id` );
ALTER TABLE `player` ADD INDEX `metadata_id` ( `metadata_id`, `team_id` ),
ADD INDEX ( `team_id` );
ALTER TABLE `equipment` ADD INDEX `metadata_id` ( `metadata_id`, `team_id` ),
ADD INDEX ( `team_id` );
ALTER TABLE `metadata` ADD CONSTRAINT `metadata_ibfk_1` FOREIGN KEY (`datatype_id`) REFERENCES `datatype` (`id`);
ALTER TABLE `team` ADD CONSTRAINT `team_ibfk_1` FOREIGN KEY (`metadata_id`) REFERENCES `metadata` (`id`);
ALTER TABLE `player` ADD CONSTRAINT `player_ibfk_1` FOREIGN KEY (`metadata_id`) REFERENCES `metadata` (`id`);
ALTER TABLE `player` ADD CONSTRAINT `player_ibfk_2` FOREIGN KEY (`team_id`) REFERENCES `team` (`id`);
ALTER TABLE `equipment` ADD CONSTRAINT `equipment_ibfk_1` FOREIGN KEY (`metadata_id`) REFERENCES `metadata` (`id`);
ALTER TABLE `equipment` ADD CONSTRAINT `equipment_ibfk_2` FOREIGN KEY (`team_id`) REFERENCES `team` (`id`);
INSERT INTO `datatype` VALUES(1,'team'),(2,'player'),(3,'equipment');
Please note that I realize I could easily speed this up by doing a UNION of two SELECTS on player and equipment for a given team id, but the ORM I'm using does not natively support UNION's and so I'd much rather try and see if I can optimize this query instead. Also I'm just plain curious.
In MySQL it's hard to optimize "OR" conditions.
One common remedy is to split the query into two simpler queries and use UNION to combine them.
(SELECT metadata.creation_date, datatype.name, player.id
FROM metadata
JOIN datatype ON datatype.id = metadata.datatype_id
JOIN player ON player.metadata_id = metadata.id
WHERE datatype.name = 'player' AND player.team_id = 1)
UNION ALL
(SELECT metadata.creation_date, datatype.name, equipment.id
FROM metadata
JOIN datatype ON datatype.id = metadata.datatype_id
JOIN equipment ON equipment.metadata_id = metadata.id
WHERE datatype.name = 'equipment' AND equipment.team_id = 1)
ORDER BY creation_date;
You have to use the parentheses so that the ORDER BY applies to the result of the UNION instead of only to the result of the second SELECT.
update: What you're doing is called Polymorphic Associations, and it's hard to use in SQL. I even call it an SQL antipattern, despite some ORM frameworks that encourage its use.
What you really have in this case is a relationship between teams and players, and between teams and equipment. Players are not equipment and equipment are not players; they don't have a common supertype. It's misleading in both an OO sense and a relational sense that you have modeled them that way.
I'd say dump your metadata and datatype tables. These are anti-relational structures. Instead, use the team_id (which I assume is a foreign key to a teams table). Treat players and equipment as distinct types. Fetch them separately if you can't use UNION in your ORM. Then combine the result sets in your application.
You don't have to fetch everything in a single SQL query.