How to Join SQL Table Content having 2 primary keys? - php

I have two SQL Tables in my database
Structure of table1 is ie customer_classification is
CREATE TABLE IF NOT EXISTS `customer_classification` (
`sid` int(5) NOT NULL,
`customer_id` varchar(15) NOT NULL,
`classification` varchar(5) NOT NULL,
`appendix_id` int(5) NOT NULL,
`bill_date` date NOT NULL );
Structure of table2 is ie customer_consumption is
CREATE TABLE IF NOT EXISTS `customer_consumption`
`sid` int(5) NOT NULL,
`customer_id` varchar(25) NOT NULL,
`bill_date` date NOT NULL,
`reading` float NOT NULL,
`consumption` float NOT NULL,
`energy_bill` float NOT NULL,
`meter_rent` float NOT NULL,
`arrear` float NOT NULL );
In both tables, primary keys are customer_id and bill_date, because in a particular month there is only bill corresponding to single customer.
Now, my problem is, I am not able to merge these tables data into one to display the whole record.
I have tried this Sql Query, have a look
select co.customer_id, co.reading, co.consumption, cl.classification
from customer_consumption as co
INNER JOIN customer_classification as cl
on cl.customer_id = co.customer_id
and month(cl.bill_date) = month(co.bill_date)
where month(co.bill_date) = month(now())
It is not giving me the accurate result

I am going to guess that the consumption table has records for each month and the classification record only has records when the classification changes. If so, you want to get the most recent classification. And you can do that as:
select co.customer_id, co.reading, co.consumption,
(select cl.classification
from customer_classification as cl
where cl.customer_id = co.customer_id and
cl.bill_date <= co.bill_date
order by cl.bill_date desc
limit 1
) as classification
from customer_consumption co
where month(co.bill_date) = month(now());

Try this SQL query
select co.customer_id, sum(co.reading), sum(co.consumption), cl.classification
from customer_consumption as co
INNER JOIN customer_classification as cl
on cl.customer_id = co.customer_id
and month(cl.bill_date) = month(co.bill_date)
where month(co.bill_date) = month(now())
group by co.customer_id

Related

MYSQL Join to get data in specific format

I've recently changed updated my database with a couple of new tables and having trouble to get(select) data from three different tables.
There are 4 stores which exchange stock with each other, it gets recorded in database.
Table 'sites' has store id and name info.
CREATE TABLE `sites` (
`id` int(10) NOT NULL,
`name` varchar(35) NOT NULL
)
Table 'stock_exchange_new' has info about stock transfer date, from
store, to store etc.
CREATE TABLE `stock_exchange_new` (
`id` int(11) NOT NULL,
`transfer_date` date NOT NULL,
`from_site` int(11) NOT NULL,
`to_site` int(11) NOT NULL,
`transfer_ref` varchar(255) NOT NULL,
`note` varchar(300) NOT NULL,
`added_by` int(11) NOT NULL,
`added_at` datetime NOT NULL,
`edited_by` int(11) NOT NULL,
`edited_at` datetime NOT NULL
)
Table 'stock_item_txns' has the information about what item was
exchanged/transferred:
CREATE TABLE `stock_item_txns` (
`id` int(11) NOT NULL,
`stock_exchange_id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
`units_per_ctn` int(11) NOT NULL,
`qty` decimal(10,2) NOT NULL,
`ctn_price` decimal(10,2) NOT NULL,
`total_price` decimal(10,2) NOT NULL
)
Now, for one particular store, I need the data shown as compare to itself, eg: for Store 1, it should be something like this:
Store Name Total_Sent Total_Received
Store2 500 200
Store3 490 580
Store4 300 400
Tried so far...
SELECT GREATEST(s1.name, s2.name) AS from_store,
LEAST(s1.name, s2.name) AS to_store,
SUM(CASE WHEN s1.name < s2.name THEN si.total_price ELSE 0 END) AS received,
SUM(CASE WHEN s1.name > s2.name THEN si.total_price ELSE 0 END) AS sent
FROM stock_exchange_new se
INNER JOIN sites s1
ON se.from_site = s1.id
INNER JOIN sites s2
ON se.to_site = s2.id
INNER JOIN stock_item_txns si
ON se.id = si.stock_exchange_id
GROUP BY GREATEST(se.from_site, se.to_site),
LEAST(se.from_site, se.to_site)
HAVING MAX(GREATEST(se.from_site, se.to_site)) = '1'
Here's the fiddle, for better understanding.
Utilising a couple of sub queries, something like this (not tested):-
SELECT s2.name AS 'Store Name',
from_site_total,
to_site_total
FROM sites s1
CROSS JOIN sites s2
LEFT OUTER JOIN
(
SELECT from_site,
to_site,
SUM(stock_item_txns.total_price) AS from_site_total
FROM stock_exchange_new
INNER JOIN stock_item_txns ON stock_exchange_new.id = stock_item_txns.stock_exchange_id
GROUP BY from_site,
to_site
) sub_from_site
ON s1.id = sub_from_site.from_site
AND s2.id = sub_from_site.to_site
LEFT OUTER JOIN
(
SELECT to_site,
from_site,
SUM(stock_item_txns.total_price) AS to_site_total
FROM stock_exchange_new
INNER JOIN stock_item_txns ON stock_exchange_new.id = stock_item_txns.stock_exchange_id
GROUP BY to_site,
from_site
) sub_to_site
ON s1.id = sub_to_site.to_site
AND s2.id = sub_to_site.from_site
WHERE s1.name = 'Store1'
AND s2.name != 'Store1'

inner join slow performance

I have a table offers that has over 100k rows so the below query is very slow (4sec - average).
SELECT cat1.id AS cat1id,
cat1.title_gr AS title,
cat1.order
FROM categories_groups_cat1 AS cat1
INNER JOIN
( SELECT categories_id, categories_groups_cat1_id FROM
categories_vs_groups
GROUP BY categories_groups_cat1_id ) AS vs
ON vs.categories_groups_cat1_id=cat1.id
INNER JOIN
( SELECT id, title_gr FROM
categories
GROUP BY title_gr ) AS cats
ON cats.id=vs.categories_id
INNER JOIN
( SELECT category_gr FROM
offers
GROUP BY category_gr ) AS offers
ON offers.category_gr=cats.title_gr
GROUP BY cat1.id
ORDER BY cat1.order ASC
table offers
`id` int(11) NOT NULL,
`title` text NOT NULL,
`description` text NOT NULL,
`image` text NOT NULL,
`price` float NOT NULL,
`start_price` float NOT NULL,
`brand` text NOT NULL
`category_gr` text NOT NULL
table categories_groups_cat1
`id` int(11) NOT NULL,
`order` int(11) NOT NULL,
`title_gr` text NOT NULL
table categories_vs_groups
`id` int(11) NOT NULL,
`categories_groups_cat1_id` int(11) NOT NULL,
`categories_id` int(11) NOT NULL
table categories
`id` int(11) NOT NULL,
`title_gr` char(255) NOT NULL
I try to choose from categories_groups_cat1 where offers exist, that's why I use the inner join. I don't know if it is completely correct. If there is another faster(performance) solution I would appreciate it
You should avoid sub-query that creates temp table. This will surely improve performance. Sub-queries that create temp table in memory kills performance, try to avoid as much as you can.
I have modified your code. There may small syntactic errors.
SELECT cat1.id AS cat1id,
cat1.title_gr AS title,
cat1.order
FROM categories_groups_cat1 AS cat1
INNER JOIN
categories_groups_cat1_id AS vs
ON vs.categories_groups_cat1_id=cat1.id
INNER JOIN
categories
AS cats
ON cats.id=vs.categories_id
INNER JOIN
offers
ON offers.category_gr=cats.title_gr
GROUP BY cat1.id,cats.title_gr, offers.category_gr
ORDER BY cat1.order ASC

Three tables inner join

I have three tables "user" , "bidding" and "item".
I need to find the query in order to get the completed item auctions for a buyer. The way how to find this in my database is the following, item.received=1 AND u.userid=X (this X will be filled in from my PHP which gives the userID of the highest bid). (note that received=1 implies that the deadline is over so this check is not necessary anymore).
Short explanation of the system: it is an auction website, where a user places bids on items and on the users personal account page I want to show the amount of auctions which he bought (and are processed, thus completed).
The 3 tables look like this:
CREATE TABLE user (
userid INT NOT NULL AUTO_INCREMENT,
username CHAR(30) NOT NULL UNIQUE,
password CHAR(32) NOT NULL,
firstname CHAR(30) NOT NULL,
lastname CHAR(30) NOT NULL,
gender CHAR(1) NOT NULL,
email CHAR(50) NOT NULL UNIQUE,
birthdate DATE NOT NULL,
addressid INT NOT NULL,
picture CHAR(50),
lastlogin TIMESTAMP NOT NULL,
role CHAR(30),
paymentid INT NOT NULL,
PRIMARY KEY (userid),
FOREIGN KEY (addressid)
REFERENCES address(addressid),
FOREIGN KEY (paymentid)
REFERENCES payment(paymentid)
);
CREATE TABLE item (
itemid INT NOT NULL AUTO_INCREMENT,
name CHAR(40) NOT NULL,
description CHAR(255) NOT NULL,
originalpurchasedate DATE,
deadline TIMESTAMP NOT NULL,
minprice DOUBLE,
received BOOLEAN NOT NULL,
dateadded TIMESTAMP NOT NULL,
openbidding BOOLEAN NOT NULL,
categoryid INT NOT NULL,
ownerid INT NOT NULL,
PRIMARY KEY (itemid),
FOREIGN KEY (categoryid)
REFERENCES category(categoryid),
FOREIGN KEY (ownerid)
REFERENCES user(userid)
);
CREATE TABLE bidding (
userid INT NOT NULL,
itemid INT NOT NULL,
amount DOUBLE,
bidtime TIMESTAMP NOT NULL,
FOREIGN KEY (userid)
REFERENCES user(userid),
FOREIGN KEY (itemid)
REFERENCES item(itemid)
);
The malfunctioning solution I have already is: the result is 3 rows and results being: 3 , 1 , 5. The solution I expect to get only has to be 1 row, containing the number of distinct items.
SELECT DISTINCT COUNT(u.userid) FROM `item` i
INNER JOIN `bidding` b ON i.itemid = b.itemid
INNER JOIN `user` u ON b.userid = u.userid
WHERE i.received=1 AND u.userid=2
GROUP BY i.itemid
You need to change your query to group on userid instead of item id, and count different items instead of different users.
SELECT DISTINCT COUNT(i.itemid) FROM `item` i
INNER JOIN `bidding` b ON i.itemid = b.itemid
INNER JOIN `user` u ON b.userid = u.userid
WHERE i.received=1 AND u.userid=2
GROUP BY u.userid

MYSQL join two tables

I have two tables one containing a selection of values in different categories and the other ‘master’ table referencing the text values by the first primary key.
Table 1
CREATE TABLE IF NOT EXISTS `defaultvalues` (
`default_ID` int(11) NOT NULL AUTO_INCREMENT,
`columnName` varchar(100) NOT NULL,
`defaultValue` varchar(100) NOT NULL,
PRIMARY KEY (`default_ID`),
UNIQUE KEY `columnName` (`columnName`,`defaultValue`)
)
Table 2
CREATE TABLE IF NOT EXISTS `master` (
`master_ID` int(11) NOT NULL AUTO_INCREMENT,
`size` int(11) NOT NULL,
`madeby` int(11) NOT NULL,
`type` int(11) NOT NULL,
`colour` int(11) NOT NULL,
`notes` text NOT NULL,
`issueDate` datetime NOT NULL,
`ceMark` text NOT NULL,
`batchNumber` text NOT NULL,
PRIMARY KEY (master_ID)
)
The master.size for each row is a P.key in the defaultvalues table.
E.g. master.colour = 234, 234=defaultvalues.defaultValue = ‘red’
E.g. master.size = 345, 345=defaultvalues.defaultValue = ‘small’
Now I would like to run a query that returns the ‘master’ table with text values in columns colour, size, type, madeby from ‘defaultvalues. defaultValue’ and ready for further processing.
I have been trying with sub queries and temp tables but I can’t get it to work
The current system relies on PHP and multiple queries and building arrays.
There has to be a more elegant solution.
I hope this makes sense.
Any hints or advice much appreciated.
Dave
You'll need to join the master table to the defaultvalues table multiple times. Something like this:
SELECT m.*, d.defaultvalue as sizevalue, d2.defaultvalue as colorvalue...
FROM master m
JOIN defaultvalues d ON m.size = d.default_id
JOIN defaultvalues d2 ON m.color = d2.default_id
...
What i did in the end.... while it works I am still not happy. There must be something better...
SELECT m.*,
(SELECT defaultValue FROM defaultvalues WHERE default_ID = m.colour) AS myColour ,
(SELECT defaultValue FROM defaultvalues WHERE default_ID = m.size) AS mySize
FROM master m
WHERE m.master_ID = 1;

How do I select MySQL records based on results from a related table?

I have two tables in this scenario: members and team_members. The members table is pretty self explanatory. The team members table stores the member's team information if they are a member of the team. If there is no row in the team members table that has a member_id of a user, then they are not in a team. What I want to do is get all the users that are not members of a team. Should I use left join, inner join, outer join, or just join? What would this query look like?
CREATE TABLE IF NOT EXISTS `members` (
`member_id` int(15) NOT NULL AUTO_INCREMENT,
`group_id` int(15) NOT NULL,
`display_name` text NOT NULL,
`email_address` text NOT NULL,
`password` text NOT NULL,
`status` tinyint(1) NOT NULL,
`activation_code` varchar(16) NOT NULL,
`date_joined` text NOT NULL,
PRIMARY KEY (`member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE IF NOT EXISTS `team_members` (
`team_member_id` int(15) NOT NULL AUTO_INCREMENT,
`member_id` int(15) NOT NULL,
`team_id` int(15) NOT NULL,
`date_joined` text NOT NULL,
`date_left` text NOT NULL,
`total_xp` int(15) NOT NULL,
PRIMARY KEY (`team_member_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
There's several ways to write this query.
To me this is the easiest to read and understand:
select * from members where member_id not in (select member_id from team_members).
This is a really simple way to write it. If you decide you want everything you can quickly comment out the where clause:
select m.* from members m left outer join team_members tm on m.member_id = tm.member_id
where tm.member_id is null
This way doesn't seem very popular from the SQL I read but I think it's straightforward:
select m.* from members m where not exists
(select member_id from team_members tm where tm.member_id = m.member_id)
On the face of it, the below query is fine
SELECT members.member_id
FROM members
LEFT OUTER JOIN team_members
ON team_members.member_id = members.member_id
WHERE team_members.member_id IS NULL
This will do, but on reading your question again, you seem to have a date_left column and if you want only those members who have not yet left a team then
SELECT members.member_id
FROM members
LEFT OUTER JOIN (SELECT *
FROM team_members
WHERE team_members.date_left != '') CURRENT_TEAMS
ON CURRENT_TEAMS.member_id = members.member_id
WHERE CURRENT_TEAMS.member_id IS NULL
SQLFiddle example
http://www.sqlfiddle.com/#!2/46b25/6/0

Categories