Mysql query, need some help. XOR operation - mysql

I have a table Books with isbn and name of books, and I have a table orders with oisbn(foreign key) and ocid (foreign key, customer ID of he who has bought the book).
A
I want to list the cids of those customers who have ordered only ocid=123 or ocid = 567,NOT both. Kind of like XOR.
How do I accomplish this in mysql? I've been thinking for a long time and I can't figure it out.

SELECT
foo
FROM
bar
WHERE ocid IN (123, 567)
GROUP BY customerId
HAVING COUNT(*) = 1

Related

Mysql multiple comparasion within one table

Mysql knowledge is far from perfect. I cannot get it all togheter so I thought I might give it a try here to understand the logic.
I've got two tables
Table: office
ID (int (pk))
name (varchar)
and
Table: staff
ID (int (pk))
office_id (int not null)
chief_id (int)
name (varchar)
wage (float)
I want to :
a) Select all people, who get paid more than their direct chiefs
b) Select a list of all offices along with a person with the highest wage in each. if more than one person has the highest wage, display them all. The office should be selected even if it has no people.
c) Select all chiefs, who have exactly one direct subordinate.
d) Select all offices sorted by total wage of people in it, descending.
Any idea where I should start ?
Thanks in advance!
Start from a)
SELECT s.*, chief.wage AS chief_wage
FROM staff s, staff chief
WHERE s.chief_id = chief.id
AND s.wage > chief.wage
Here we use self join to add the chief's data to all the staff. And then just compare salaries.

Writing pseudo-sql statement

Today I passed an interview and thez ask me this :
Write pseudo SQl statements to create database tables to store the products of a basic websho. Each product has a name, a price, a creation date ans may belong to several categories. Categories have a name and a flag to indicate whether the category is private or public.
Also:
Write a SQL query to find the list of products that belong to more than 5 public categories.
My knowledge of sql is pretty limited, i just pass 2 hours searching on the web for pseudo-sql statements without result.
Can someone explain me what it is and eventually respond to the answers so i'll know ? (interview already fail so you dont do my homework ahahah)
Thanks!
product(pro_id, pro_name, price, date)
pro_id is the Primary key
category(cat_id, cat_name, cat_flag, cat_type)
cat_id s the primary key
procat(pro_id, cat_id)
pro_id, cat_id are primary key together
Query:
select pro_id from procat where cat_id in (select cat_id from category where cat_type = 'public') group by pro_id having count(*)>2

Alternatives to junction table?

I'm designing a relational database tables for storing data about eCommerce scenario where I need to store
List of Products purchased by a user
List of users who purchased a particular product.
Its a many to many relationship.
So far I could only thinking of doing this.
create a table for storing orders
table recordorders(
userID // foreign key from users table
productID, // foreign key from products table
dateofpurchase,
quantity,
price_per_unit,
total_amount
)
It will act like a junction table.
Is this a good approach and are there any other methods than junction table that are more effective and efficient for querying ?
Your bullets describe two tables, not one. Your junction table is not properly described as two lists. It is a set of order info rows. The junction table you gave holds rows where "user [userID] purchased product [productID] on ...". Ie it records order info. (Combinations of user, product, date, etc of orders.) Given a user or product, you can get the corresponding bullet table by querying the order info table.
However your table probably needs another column that is a unique order id. Otherwise it cannot record that there are two orders that are alike in all those columns. (Eg if the same person buys the same product on the same date in the same quantity, price and total.) Ie its rows probably aren't 1:1 with orders. That's why above I called it an order info table rather than an order table. It records that some order had those properties; but it doesn't record distinct orders if there can be orders with the same info. It's really a many-to-many-to-etc (for every column) association. That is why an order id gets picked as a unique name for an order as further info. This new table would be called an entity table, not a junction or association table. It holds rows where "in order [id] user [user] purchased ...".
PS An order is usually something that can be characterized as an association on/among/between an order id, user, set of order-lines (product, quantity, price & total), and other stuff (date, grand total, etc). The orders are usually relationally characterized by an order entity table on order id with its user, date etc plus an order-line association table on order ids and their order-line info.
PPS It's time for you to read a book about information modeling and database design.
You don't "store" those two things in a table (Junction, or otherwise), you discover them from the raw ("Fact") data:
Using your proposed table:
List of Products purchased by a user:
SELECT productID
FROM recordorders
WHERE userID = 123;
List of users who purchased a particular product:
SELECT userID
FROM recordorders
WHERE productID = 987;

How to select all subjects that are taken by all students such that all students took grade > 50 in them

I have a table called records containing 3 columns: student, subject and grade
Each {x,y,z} entry in the table means student x took class y with grade z. If student x didn't take class y then the entry doesn't exist in the table. So this table is like a university record of all students and the subjects taken by them.
I want to select all subjects that are taken by all students such that the grades of all students in these subjects are above 60.
I tried creating the table
CREATE TABLE temp SELECT subject FROM records WHERE grade > 60;
Then I used temp to create a new table that has subject and count, where count counts the number of students that took that subject, and then I deleted all rows that have count< number of students. But I know this is very inefficient.
How can I do this more efficiently using MySQL ?
Also if you can provide me with good MySQL resource/tutorial link so that I can practice, I would be thankful. I am new to MySQL and I am working on large databases and I need to make my queries more efficient and straight forward.
How about
SELECT subject FROM records
WHERE subject NOT IN
(
SELECT subject FROM records
WHERE grade <=60
)
AND subject IN
(
SELECT subject FROM records
GROUP BY subject
HAVING count(*) = (SELECT COUNT(DISTINCT student) FROM records)
)
As further reading I'd recommend this and this
EDITED: now includes "subjects taken by ALL students"
I would use the no exists clause because it simply confirms if there are no records within the subquery, but will not try to obtain the resultset. It is more effective than a not in clause.
SELECT subject FROM records r1
WHERE subject NOT EXISTS
(
SELECT 1 FROM records r2
WHERE r2.subject=r1.SUBJECT AND grade<=60
)

In MYSQL, how to summarise query results based on the parameters not specified in the query?

I have a MySQL table with around 4 million+ rows. Let us say the table is as follows:
Columns in table Person:
Id
Name
Age
Marital Status
Education Level
'Location Country'
'Description'
When I run a query based on Age, I also want to have a summary count of people with the same age in different marital status and also with different 'Education Level' and 'Location Country'.
When I run a query based on Age and Education Level, I also want to have a summary count of people with the same age and Education Level in different marital status and also with different 'Location Country'.
For example, the query issued would be SELECT * FROM Person WHERE Age = 27;. I also want results that would be produced by SELECT Education Level, COUNT(*) FROM Person WHERE Age = 27 GROUP BY Education Level; and SELECT Location Country, COUNT(*) FROM Person WHERE Age = 27 GROUP BY Location Country;
Also, this becomes more challenging for me when I have to do a search based on keywords on description and want a summary count on each of the other columns. The application I am developing is a sort of search engine. This can be seen in sites like Ebay,
I can possibly run these queries separately. But, with 4 million rows, the GROUP BY query will take substantial amount of time. This is an internet application and the query should complete within few seconds.
Any help would be much appreciated.
You can do both in one query
SELECT p.*, count(p2.id)
FROM Person p, Person p2
WHERE p2.Age = p.age and p2.marital != p.marital and p1.education != p2.education
GROUP BY p1.id
In such situation, I would suggest to save data in a memcache cache. You can expire cache if new data inserted to table or after some expiration time, to avoid long query execution. Another improvement would be using a LIMIT to reduce number of row returned by DB like this:
SELECT p.*, count(p2.id)
FROM Person p, Person p2
WHERE p2.Age = p.age and p2.marital != p.marital and p1.education != p2.education
GROUP BY p1.id
LIMIT 10
From what you are describing, I would have a separate aggregate table to query directly from that has those "roll-up" stats you want. How frequent is the "Person" table getting added to / changed. If you are only storing a person's "Age", what is the basis of the age if no date, and you add the person again in future they would have multiple records... such that
At age X, so many people were married (or not) and had this level of education.
At age Y, so many people... etc..
I would create a summary table, something like
create table AgeStat (
age int,
married int,
single int,
divorced int,
HighSchool int,
Associates int,
Bachelors int,
Masters int,
Doctorate int )
Then, add a trigger to the person table such that during insert (or inclusive of update/delete as needed), the new record just adds 1 to each respective count applicable.
Then, for your web app, it would be instantaneous to grab one record from this summary table where age = 27 and you have ALL your classification stats.
However, if you distinctly wanted to know how many Married with Masters degree, you would have to roll back to master person list.
Alternatively, you could do a similar pre-aggregation but down a level of granularity something like
create table AgeStat (
age int,
maritalstat int, -- but I would actually use an enumerated value for marital status
educationlevel int, -- and education level vs a hard description of each.
peoplecount int )
and likewise have a trigger that updates the count based on the two combination elements per age. Then, if you wanted the total "Married", you can sum(peoplecount) for age = 27 and maritalstat=(enumerator for "married" value)
Good luck, and hope it helps alternative solution for you.