How do I write this MySQL query to get the correct information? (Subquery, multiple subqueries) - sql

Here is the query that I am working on:
SELECT `unitid`, `name` FROM apartmentunits
WHERE aptid = (
SELECT `aptid` FROM rentconditionsmap WHERE rentcondid = 1 AND condnum = 1
)
What I am having trouble figuring out is how to write this to add more rentcondition limiters to filter this list down more.
SELECT `aptid` FROM rentconditionsmap WHERE rentcondid = 1 AND condnum = 1
Data:
CREATE TABLE IF NOT EXISTS `rentconditionsmap` (
`rcid` bigint(10) unsigned NOT NULL AUTO_INCREMENT,
`rentcondid` int(3) unsigned NOT NULL,
`condnum` tinyint(3) unsigned NOT NULL,
`aptid` bigint(10) unsigned DEFAULT NULL,
PRIMARY KEY (`rcid`), KEY `aptid` (`aptid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=18 ;
INSERT INTO `rentconditionsmap`
(`rcid`, `rentcondid`, `condnum`, `aptid`)
VALUES
(1, 1, 1, 1),
(2, 2, 1, 1),
(3, 3, 0, 1),
(4, 4, 1, 1),
(5, 8, 0, 1);
CREATE TABLE IF NOT EXISTS `apartmentunits` (
`unitid` bigint(10) NOT NULL AUTO_INCREMENT,
`aptid` bigint(10) NOT NULL,
`name` varchar(6) NOT NULL,
`verified` tinyint(1) NOT NULL DEFAULT '0',
`rentcost` int(4) unsigned DEFAULT NULL,
`forrent` tinyint(1) NOT NULL DEFAULT '0',
`unittypekey` varchar(2) DEFAULT NULL,
`sqft` smallint(6) DEFAULT NULL,
PRIMARY KEY (`unitid`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=121 ;
INSERT INTO `apartmentunits`
(`unitid`, `aptid`, `name`, `verified`, `rentcost`, `forrent`, `unittypekey`, `sqft`)
VALUES
(1, 1, '3', 1, 540, 0, '2B', NULL),
(2, 1, '5', 1, NULL, 0, '2B', NULL),
(3, 1, '7', 1, NULL, 0, '2B', NULL),
(53, 1, '1', 1, NULL, 0, '2B', NULL),
(54, 1, '2', 1, NULL, 0, '2B', NULL),
(55, 1, '4', 1, 570, 0, '2B', NULL),
(56, 1, '6', 1, NULL, 0, '2B', NULL),
(57, 1, '8', 1, NULL, 0, '2B', NULL),
(58, 1, '9', 1, NULL, 0, '2B', NULL),
(59, 1, '10', 1, NULL, 0, '2B', NULL),
(60, 1, '11', 1, NULL, 0, '2B', NULL);

As Eric J said as a comment:
Try changing = to IN
SELECT `unitid`, `name` FROM apartmentunits
WHERE `aptid` IN (
SELECT `aptid` FROM rentconditionsmap WHERE rentcondid = 1 AND condnum = 1
)

why not:
SELECT unitid, name
FROM apartmentunits a
INNER JOIN rentconditionsmap r on a.aptid = r.aptid
WHERE (rentcondid = 1 and condnum = 1) OR (rentcondid = 2 and condnum = 2)

Using ANSI-92 join syntax:
SELECT au.unitid,
au.name
FROM APARTMENTUNITS au
JOIN RENTCONDITIONSMAP rcm ON rcm.aptid = au.aptid
AND rcm.rentcondid = 1
AND rcm.condnum = 1

Use a JOIN (below is TSQL sintax for a join, or you can use the explicit INNER JOIN).
SELECT apartmentunits.unitid, apartmentunits.name
FROM apartmentunits, rentconditionsmap
WHERE apartmentunits.aptid = rentconditionsmap.aptid
AND rentconditionsmap.rentcondid = 1
AND rentconditionsmap.condnum = 1
-- AND whatever else...

Related

Calculating distance then finding people within specific range of given latitude and longitude in SQL

I'm calculating distance between people like below:
select (
point(
(select latlng from user_account where name = 'Manel')
)
<#>
point(
(select latlng from user_account where name = 'Ben')
)
) as distance
which will then output like this:
The scenario is that if Manel wants to get all the people within 5 km, how to write the SQL Query?
(We are using name as the input parameter and there is Latlng stored in the table the type is point)
Here is the sample data:
DROP TABLE IF EXISTS "user_account";
DROP SEQUENCE IF EXISTS user_account_id_seq;
CREATE SEQUENCE user_account_id_seq INCREMENT 1 MINVALUE 1 MAXVALUE 2147483647 START 1 CACHE 1;
CREATE TABLE "public"."user_account" (
"id" integer DEFAULT nextval('user_account_id_seq') NOT NULL,
"first_name" character varying(64) NOT NULL,
"last_name" character varying(64) NOT NULL,
"nickname" character varying(64),
"age" character varying(32),
"details" text,
"email" character varying(128),
"last_login_time" timestamp,
"create_time" timestamp,
"popularity" numeric(5,2),
"interested_in_relation" character varying(32),
"interested_in_gender" character varying(32),
"tag" character varying(256),
"point" integer,
"membership_type" character varying(32),
"account_status" character varying(32),
"account_name" character varying(32),
"password" character varying(32),
"latlng" point,
CONSTRAINT "user_account_ak_1" UNIQUE ("email"),
CONSTRAINT "user_account_pk" PRIMARY KEY ("id")
) WITH (oids = false);
INSERT INTO "user_account" ("id", "first_name", "last_name", "nickname", "age", "details", "email", "last_login_time", "create_time", "popularity", "interested_in_relation", "interested_in_gender", "tag", "point", "membership_type", "account_status", "account_name", "password", "latlng") VALUES
(8, 'Addison', 'Davies', 'Addison Davies', NULL, NULL, 'addison.davies#example.com', '2021-04-15 15:18:01.982', '2021-04-15 15:18:01.982', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'blackswan592', 'ajax', '(-0.1277,51.5073)'),
(10, 'Manel', 'Almeida', 'Manel Almeida', NULL, NULL, 'manel.almeida#example.com', '2021-04-15 15:28:25.85', '2021-04-15 15:28:25.85', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'orangekoala363', 'clarissa', '(-73.5959,-50.4127)'),
(11, 'Ben', 'Tidemann', 'Ben Tidemann', NULL, NULL, 'ben.tidemann#example.com', '2021-04-15 15:28:37.776', '2021-04-15 15:28:37.776', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'sadfrog539', 'morpheus', '(51.8397,-147.6034)'),
(12, 'Dieter', 'Campos', 'Dieter Campos', NULL, NULL, 'dieter.campos#example.com', '2021-04-15 15:28:38.939', '2021-04-15 15:28:38.939', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'silvercat262', 'irish', '(-72.8698,-139.8647)'),
(13, 'Girão', 'Martins', 'Girão Martins', NULL, NULL, 'girao.martins#example.com', '2021-04-15 15:33:58.464', '2021-04-15 15:33:58.464', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'yellowgoose254', 'shotgun', '(-69.7453,-170.808)'),
(14, 'Tammy', 'Hale', 'Tammy Hale', NULL, NULL, 'tammy.hale#example.com', '2021-04-15 15:33:59.925', '2021-04-15 15:33:59.925', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'beautifulzebra663', 'james007', '(25.8726,55.214)'),
(15, 'Hudson', 'Miller', 'Hudson Miller', NULL, NULL, 'hudson.miller#example.com', '2021-04-15 15:34:33.501', '2021-04-15 15:34:33.501', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'angrygorilla353', '414141', '(-59.3342,-90.5217)'),
(16, 'Apolline', 'Renard', 'Apolline Renard', NULL, NULL, 'apolline.renard#example.com', '2021-04-15 15:35:02.523', '2021-04-15 15:35:02.523', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'smallleopard612', '143143', '(0.6492,18.2443)'),
(17, 'Afet', 'Demirel', 'Afet Demirel', NULL, NULL, 'afet.demirel#example.com', '2021-04-15 15:35:08.072', '2021-04-15 15:35:08.072', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'happyfrog105', 'niners', '(75.6424,138.3762)'),
(18, 'Necati', 'Numanoğlu', 'Necati Numanoğlu', NULL, NULL, 'necati.numanoglu#example.com', '2021-04-15 15:35:09.242', '2021-04-15 15:35:09.242', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'bluefish441', 'beavis', '(70.8946,-7.5597)'),
(19, 'Danny', 'Soto', 'Danny Soto', NULL, NULL, 'danny.soto#example.com', '2021-04-15 15:35:10.32', '2021-04-15 15:35:10.32', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'blackpanda305', 'pentium', '(-79.9498,-171.0615)'),
(20, 'Jesse', 'Aro', 'Jesse Aro', NULL, NULL, 'jesse.aro#example.com', '2021-04-15 15:35:11.457', '2021-04-15 15:35:11.457', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'beautifulkoala522', 'lonely', '(-7.8552,-1.1386)'),
(21, 'Lucy', 'Zhang', 'Lucy Zhang', NULL, NULL, 'lucy.zhang#example.com', '2021-04-15 15:35:12.406', '2021-04-15 15:35:12.406', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'brownladybug716', 'beautiful', '(63.4473,52.2642)'),
(22, 'محیا', 'گلشن', 'محیا گلشن', NULL, NULL, 'mhy.glshn#example.com', '2021-04-15 15:35:13.503', '2021-04-15 15:35:13.503', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'browncat969', 'jorge', '(28.8925,109.2892)'),
(23, 'Jorge', 'Roman', 'Jorge Roman', NULL, NULL, 'jorge.roman#example.com', '2021-04-15 16:20:55.996', '2021-04-15 16:20:55.996', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'crazyostrich244', 'yyyyyyyy', '(-20.7278,-17.5191)'),
(24, 'Clayton', 'Jacobs', 'Clayton Jacobs', NULL, NULL, 'clayton.jacobs#example.com', '2021-04-15 16:20:57.276', '2021-04-15 16:20:57.276', NULL, NULL, NULL, NULL, 0, 'normal', 'active', 'happyrabbit978', 'killers', '(88.5666,-97.9353)');
Here is a query that returns all the people within a range of 5 km for a given name:
SELECT
ua1.id AS id1,
ua1.first_name AS name1,
ua1.latlng AS latlng1,
ua2.id AS id2,
ua2.first_name AS name2,
ua2.latlng AS latlng2,
(ua1.latlng <#> ua2.latlng) * 1.609344 as distance
FROM user_account ua1
JOIN user_account ua2 ON ua1.id <> ua2.id
WHERE ua1.first_name = 'Manel'
AND (ua1.latlng <#> ua2.latlng) * 1.609344 < 5;
Please note that the <#> operator returns miles instead of kilometers, so additional conversion is necessary, as shown above.
Thanks for Karol's nice answer! I also come up with a solution.
WITH latlng1 AS (
SELECT
latlng
FROM
user_account
WHERE
first_name = 'Manel'
),
result AS (
SELECT
(
point(
(latlng1.latlng)
) <#> point(
(user_account.latlng)
)
) as distance,
user_account.nickname
from
user_account,
latlng1
)
select
*
from
result
where
distance < 5 / 1.609344 AND nickname <> 'Manel Almeida'

SQL - Syntax Error (Problems with PK and FK)

I a want to fill a DB with SQL, but I get syntax errors. I think the problem is in PK and FK. Can you please take a look, what am I doing wrong in my approach.
Thank you very much!
SQL Code
CREATE TABLE TheOrder
(
ordereditem VARCHAR(50) NOT NULL PRIMARY KEY,
productid INTEGER NOT NULL,
productwholesaler VARCHAR (50) NOT NULL
);
INSERT INTO TheOrder VALUES
('overwatch', 1, 'blizzard'),
('deathadder', 2, 'razer'),
('xboxcontroller', 3, 'microsoft'),
('kraken', 4, 'razer'),
('diablo3', 5, 'blizzard'),
('warcraft3remastered', 6, 'blizzard'),
('fallout3', 7, 'bethesda'),
('pixel', 8, 'google'),
('aspire5', 9, 'acer'),
('destiny2', 10 ,'activision');
CREATE TABLE TheStock
(
counteditems INTEGER NOT NULL,
isInStock BOOLEAN NOT NULL,
stockID INTEGER NOT NULL PRIMARY KEY,
fkordereditem VARCHAR,
CONSTRAINT fkordereditem FOREIGN KEY (fkordereditem) REFERENCES TheOrder(ordereditem)
);
INSERT INTO TheStock VALUES
('overwatch', 5, true, 1),
('deathadder', 2, true, 2),
('xboxcontroller', 0, false, 3),
('kraken', 0, false, 4),
('diablo3', 5, true, 5),
('warcraft3remastered', 0, false, 6),
('fallout3', 7, true, 7),
('pixel', 8, true, 8),
('aspire5', 0, false, 9),
('destiny2', 10 , true, 10);
CREATE TABLE TheReorder
(
fkproductid VARCHAR,
fkproductwholesaler VARCHAR,
howmanydays INTEGER NOT NULL,
mobilenumber INTEGER PRIMARY KEY,
postcode INTEGER
CONSTRAINT fkproductid FOREIGN KEY (fkproductid) REFERENCES TheOrder(productid),
CONSTRAINT fkproductwholesaler FOREIGN KEY (fkproductwholesaler) REFERENCES TheOrder (productwholesaler)
);
INSERT INTO TheReorder VALUES
('overwatch', 'blizzard', 0, 649494, 1020),
('deathadder', 'razer', 0, 498494, 1150),
('xboxcontroller', 'microsoft', 3, 948849, 71000),
('kraken', 'razer', 5, 249489, 32009),
('diablo3', 'blizzard', 0, 194984, 29000),
('warcraft3remastered', 'blizzard', 12, 398484, 11000),
('fallout3', 'bethesda', 0, 694895, 42132),
('pixel', 'google', 0, 873243, 9201),
('aspire5', 'acer', 15, 988564, 10020),
('destiny2', 'activision', 0, 745637, 10090);
CREATE TABLE TheCourier
(
couriername VARCHAR(50) NOT NULL PRIMARY KEY,
couriercompany VARCHAR (50) NOT NULL,
courieravailable BOOLEAN NOT NULL
);
INSERT INTO TheCourier VALUES
('jon snow', 'the wall', false),
('ned stark', 'winterfell', false),
('elon musk', 'space x', true),
('yusaku maezawa', 'zozo', false),
('hokusai katsushika', 'the great wave', false),
('ragnar lothbrok', 'vikings', false),
('jax teller', 'sons of anarchy', true),
('harvey specter', 'post', true),
('michael corleone', 'ups', false),
('tommy shelby', 'fedex', false);
CREATE TABLE ThePenaltyInfo
(
fkhowmanydays INTEGER,
fkproductidpen INTEGER,
feetopay INTEGER NOT NULL,
currencie VARCHAR(50) NOT NULL,
penaltyid INTEGER NOT NULL PRIMARY KEY,
CONSTRAINT fkhowmanydays FOREIGN KEY (fkhowmanydays) REFERENCES TheReorder(howmanydays),
CONSTRAINT fkproductidpen FOREIGN KEY (fkproductidpen) REFERENCES TheOrder(productid)
);
INSERT INTO ThePenaltyInfo VALUES
(0, 1, 0, 'euro', 1),
(0, 2, 0, 'euro', 2),
(3, 3, 50, 'usd', 3),
(5, 4, 90, 'usd', 4),
(0, 5, 0, 'ron', 5),
(12, 6, 30, 'ron', 6),
(0, 7, 0, 'euro', 7),
(0, 8, 0, 'euro', 8),
(15, 9, 800, 'euro', 9),
(0, 10, 0, 'euro', 10);
CREATE TABLE TheShipementList
(
fkcouriercompany VARCHAR,
fkproductidship VARCHAR,
shipementid INTEGER NOT NULL PRIMARY KEY,
shipementaddress VARCHAR(50) NOT NULL,
customernumber INTEGER NOT NULL,
CONSTRAINT fkcouriercompany FOREIGN KEY (fkcouriercompany) REFERENCES TheCourier(couriercompany),
CONSTRAINT fkproductidship FOREIGN KEY (fkproductidship) REFERENCES TheOrder(productid)
);
INSERT INTO TheShipementList VALUES
('space x', 1, 1, 'Palma de Mallorca', 1994007),
('space x', 2, 1, 'Palma de Mallorca', 1994007),
('space x', 3, 1, 'Palma de Mallorca', 1994007),
('space x', 4, 1, 'Palma de Mallorca', 1994007),
('space x', 5, 1, 'Palma de Mallorca', 1994007),
('space x', 6, 1, 'Palma de Mallorca', 1994007),
('space x', 7, 1, 'Palma de Mallorca', 1994007),
('space x', 8, 1, 'Palma de Mallorca', 1994007),
('space x', 9, 1, 'Palma de Mallorca', 1994007),
('space x', 10, 1, 'Palma de Mallorca',1994007);
ERROR MESSAGE
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near '
CONSTRAINT FK FOREIGN KEY (fkordereditem) REFERENCES TheOrder(ordereditem) ' at line 6
You have multiple problems :
you forgot a comma after postcode INTEGER
You forgot to specify the length for some VARCHAR column definitions (this is mandatory, if you don't want to specify a length use VARCHAR(MAX).
column order in some INSERT statements does not match the table definition (you need to respect the order, or specify the column names in the INSERT statement)
you are trying to set foreign keys that references non primary key, if you want to do this you need to define referenced columns as UNIQUE
it seems you are trying to insert duplicates for some primary key values
you are referencing foreign keys with different data types then your column
I found the error. The assigned types have not been the same for the FK and for the referenced attribute.
CREATE TABLE TheOrder
(
productid INTEGER NOT NULL PRIMARY KEY,
ordereditem VARCHAR(50) NOT NULL,
productwholesaler VARCHAR (50) NOT NULL
);
CREATE TABLE TheStock
(
counteditems INTEGER NOT NULL,
isInStock BOOLEAN NOT NULL,
stockID INTEGER NOT NULL PRIMARY KEY,
testid INTEGER,
CONSTRAINT testid FOREIGN KEY (testid) REFERENCES TheOrder(productid)
);

Get the right data for a specific id

I want to get the prices for each product for a specific place. Here's what I have done so far.
SELECT pl.id AS place_id,
pl.data_name AS place_name,
pp.data_price AS product_price,
pp.date_updated AS price_updated
FROM places AS pl
JOIN products AS pr
ON pl.id = pr.id_place
JOIN products_prices AS pp
WHERE pp.id_product = '30'
GROUP BY pl.id, pp.data_price, pp.date_updated
ORDER BY pp.data_price DESC, pp.date_updated DESC
As you can see from the image above, product_price and price_updated are all the same. place_name also shows 3 places that does not have the product with id 30.
Here's how it should looks like (cheapest, most recent product purchase on top):
place_id place_name product_price price_updated
3 ICA Maxi 4.95 2018-05-16
1 ICA Supermarket 5.90 2018-05-27
26 ICA Skutan 6.50 2018-05-29
Here's the database structure:
CREATE TABLE IF NOT EXISTS `places` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`data_name` tinytext NOT NULL,
`data_address` text,
`data_address_city` tinytext NOT NULL,
`data_coordinates` varchar(50) NOT NULL,
UNIQUE KEY `id` (`id`)
);
INSERT INTO `places` (`id`, `data_name`, `data_address`, `data_address_city`, `data_coordinates`) VALUES
(1, 'ICA Supermarket', 'Björkhagsgatan 9', 'Skoghall', '59.324971,13.467291'),
(2, 'ICA Maxi', 'Bergviks Köpcentrum', 'Karlstad', '59.376563,13.428787'),
(3, 'ICA Kvantum', 'Bivägen 11', 'Hammarö', '59.343388,13.504583'),
(4, 'IKEA', 'Bergviksvägen 43', 'Karlstad', '59.379032,13.420646'),
(5, 'Karlstad Naprapatklinik', 'Västra Torggatan 15', 'Karlstad', '59.381379,13.501683'),
(9, 'Besök i Borgvik AB', '', 'Borgvik', '59.348261,12.954707'),
(23, 'Mariebergsskogen', '', 'Karlstad', '59.369403,13.486485'),
(24, 'Happy Price', 'Brehogsvägen 20', 'Tanumshede', '58.723730,11.344768'),
(25, 'Trekanten Kök & Bar', 'Parkvägen 2', 'Hamburgsund', '58.552733,11.270998'),
(26, 'ICA Skutan', 'Strandvägen', 'Hamburgsund', '58.552122,11.270898');
CREATE TABLE IF NOT EXISTS `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_place` int(11) NOT NULL,
`data_barcode` text NOT NULL,
`data_name` text NOT NULL,
`data_weight` text NOT NULL,
`data_weight_type` text NOT NULL,
UNIQUE KEY `id` (`id`)
);
INSERT INTO `products` (`id`, `id_place`, `data_barcode`, `data_name`, `data_weight`, `data_weight_type`) VALUES
(7, 3, '7311070008494', 'Pågen Lantbröd', '650', 'g'),
(8, 3, '7310618084808', 'Grumme citronsåpa', '750', 'ml'),
(9, 3, '7318690079835', 'ICA Basic toalettpapper', '', ''),
(12, 1, '7318690134640', 'ICA Basic Milk & Caramel', '100', 'g'),
(18, 3, '7310380512103', 'ICA Home dishmatic refillsvamp', '', ''),
(19, 3, '7340109200684', 'Plastkasse', '', ''),
(20, 1, '7310751163903', 'ICA tonfisk filébitar i vatten', '185', 'g'),
(24, 1, '7310865001818', 'Arla mellanmjölk', '1.5', 'kg'),
(25, 1, '7318690079712', 'ICA kattsand (klumpbildande, ej parfym)', '6', 'kg'),
(29, 26, '3068320055008', 'Evian mineralvatten', '500', 'mL'),
(30, 26, '7318690134640', 'ICA Basic Milk & Caramel', '100', 'g'),
(33, 2, '0', 'ICA Basic Milk & Caramel', '100', 'g');
CREATE TABLE IF NOT EXISTS `products_prices` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`id_product` int(11) NOT NULL,
`id_transaction` int(11) NOT NULL,
`data_price` decimal(10,2) NOT NULL,
`data_amount` tinytext NOT NULL,
`data_discount` tinytext,
`data_discount_amount` tinytext,
`data_discount_sum` tinytext,
`data_pant` tinytext,
`date_updated` date NOT NULL,
UNIQUE KEY `id` (`id`)
);
INSERT INTO `products_prices` (`id`, `id_product`, `id_transaction`, `data_price`, `data_amount`, `data_discount`, `data_discount_amount`, `data_discount_sum`, `data_pant`, `date_updated`) VALUES
(1, 1, 907, 18.90, '4', '', '2', '30', NULL, '2018-05-18'),
(2, 12, 907, 4.90, '4', '', '', '', NULL, '2018-05-18'),
(5, 4, 904, 18.90, '1', '', '', '', NULL, '2018-05-18'),
(6, 14, 904, 14.90, '1', '', '', '', NULL, '2018-05-18'),
(17, 19, 936, 2.00, '1', '', '', '', NULL, '2018-05-21'),
(18, 21, 947, 23.90, '1', '', '', '', NULL, '2018-05-23'),
(19, 22, 947, 10.50, '1', '', '', '', NULL, '2018-05-23'),
(20, 20, 947, 15.90, '1', '', '', '', NULL, '2018-05-23'),
(21, 13, 947, 49.90, '1', '', '', '', NULL, '2018-05-23'),
(22, 23, 948, 14.90, '1', '', '', '', NULL, '2018-05-24'),
(23, 24, 961, 14.90, '1', NULL, NULL, NULL, NULL, '2018-05-27'),
(24, 12, 961, 5.90, '4', NULL, NULL, NULL, NULL, '2018-05-27'),
(32, 28, 967, 8.50, '2', NULL, NULL, NULL, NULL, '2018-05-28'),
(33, 29, 972, 12.90, '1', NULL, NULL, NULL, '1', '2018-05-29'),
(34, 30, 973, 6.50, '2', NULL, NULL, NULL, NULL, '2018-05-29'),
(35, 31, 976, 10.00, '1', NULL, NULL, NULL, NULL, '2018-05-30'),
(36, 32, 976, 10.00, '1', NULL, NULL, NULL, NULL, '2018-05-30');
As mentioned in my comment to your request: You forgot the ON clause that links the products_prices table to the other two.
MariaDB nastily converts your inner join into a cross join instead of raising an error as it should. What this gives you is: select all prices for product 30 combined with all places multiplied with the number of products therein, instead of just combined with the product's place.
By the way: I'd put table products first in the FROM clause, because this is the base (you want product 30). Then join the product's prices and the product's place.
There is no reason to group your data, as you only want to list the prices. You are not aggregating anything. So you get:
SELECT
pl.id AS place_id,
pl.data_name AS place_name,
pp.data_price AS product_price,
pp.date_updated AS price_updated
FROM products AS pr
JOIN products_prices AS pp ON pp.id_product = pr.id
JOIN places pl ON pl.id = pr.id_place
WHERE pr.id = 30
ORDER BY pp.data_price DESC, pp.date_updated DESC;
Try this:
SELECT PL.id AS place_id,
PL.data_name AS place_name,
PP.data_price AS product_price,
PP.date_updated AS price_updated
FROM products_prices PP
JOIN products PR
ON PR.id = PP.id_product
JOIN places PL
ON PL.id = PR.id_place
WHERE PR.id = '30'

How can I prevent SQL Server from squaring the number of rows scanned?

I'm running a query over a table variable that holds 22 227 rows. The query used to take 2-3 seconds to complete (which I still think is too slow) but since I added another field to the ORDER BY clause in DENSE_RANK() it now completes in 4.5 minutes!
If I include [t2].[aisdt] with or without [t2].[aiID], the execution plan shows that it's scanning 494 039 529 rows, which is 22 227 squared. The following query generates the correct results, just much too slowly to be useful.
SELECT MAX([t].[SetNum]) OVER (PARTITION BY NULL) AS [MaxSet]
,*
FROM (
SELECT DENSE_RANK() OVER (ORDER BY [t2].[aisdt], [t2].[aiID]) AS [SetNum]
,[t2].*
FROM (
SELECT [aiID]
,COUNT(DISTINCT [acID]) AS [noac]
FROM #Temp
GROUP BY [aiID]
) [t1]
JOIN #Temp [t2]
ON [t2].[aiID] = [t1].[aiID]
WHERE [t1].[noac] < [t2].[asm]
) [t]
Just to be clear, the culprit is the bold section in "DENSE_RANK() OVER (ORDER BY [t2].[aisdt], [t2].[aiID])". Removing this field (which needs to remain) drops the execution time back down to 2-3 seconds. I think it might have something to do with JOINing the table to itself on [aiID] but not [aisdt].
How can I speed this query up to complete in the same time as before, or less?
EDIT
Table definition:
DECLARE #Temp TABLE (
[aiID] INT NOT NULL INDEX [IX_Temp_aiID] -- not unique
,[aisdt] DATETIME NOT NULL INDEX [IX_Temp_aisdt] -- not unique
,[asm] INT NOT NULL
,[cpcID] INT NULL
,[cpce] VARCHAR(10) NULL
,[acID] INT NULL
,[ctvID] INT NULL
,[ct] VARCHAR(100) NULL
,[_36_other_non_matched_fields_] VARCHAR(MAX)
,UNIQUE ([aiID], [cpcID], [cpce], [acID], [ctvID], [ct])
)
[aisdt] is unique per [aiID], but there can be multiple [aiID]s with the same [aisdt].
INSERT INTO #TEMP
VALUES (64, '2017-03-23 10:00:00', 1, 17, '', NULL, NULL, NULL, 'blah')
,(64, '2017-03-23 10:00:00', 1, 34, '', NULL, NULL, NULL, 'blah')
,(99, '2017-04-08 09:00:00', 1, 25, 'Y', NULL, NULL, NULL, 'blah')
,(99, '2017-04-08 09:00:00', 1, 16, 'Y', NULL, NULL, NULL, 'blah')
,(99, '2017-04-08 09:00:00', 1, 76, 'Y', NULL, NULL, NULL, 'blah')
,(99, '2017-04-08 09:00:00', 1, 82, 'Y', NULL, NULL, NULL, 'blah')
,(42, '2017-04-14 16:00:00', 2, 32, '', 32, NULL, NULL, 'blah')
,(42, '2017-04-14 16:00:00', 2, 32, '', 47, NULL, NULL, 'blah')
,(42, '2017-04-14 16:00:00', 2, 47, '', 32, NULL, NULL, 'blah')
,(42, '2017-04-14 16:00:00', 2, 47, '', 47, NULL, NULL, 'blah')
,(54, '2017-03-23 10:00:00', 1, 17, '', NULL, NULL, NULL, 'blah')
,(54, '2017-03-23 10:00:00', 1, 34, '', NULL, NULL, NULL, 'blah')
,(89, '2017-04-08 09:00:00', 1, 25, 'Y', NULL, NULL, NULL, 'blah')
,(89, '2017-04-08 09:00:00', 1, 16, 'Y', NULL, NULL, NULL, 'blah')
,(89, '2017-04-08 09:00:00', 1, 76, 'Y', NULL, NULL, NULL, 'blah')
,(89, '2017-04-08 09:00:00', 1, 82, 'Y', NULL, NULL, NULL, 'blah')
,(32, '2017-04-14 16:00:00', 3, 32, '', 32, NULL, NULL, 'blah')
,(32, '2017-04-14 16:00:00', 3, 32, '', 47, NULL, NULL, 'blah')
,(32, '2017-04-14 16:00:00', 3, 47, '', 32, NULL, NULL, 'blah')
,(32, '2017-04-14 16:00:00', 3, 47, '', 47, NULL, NULL, 'blah')
It must be sorted by [aisdt] (datetime) first, then [aiID], then numbered into sets based on [aiID].
I want to see:
5, 1, 54, '2017-03-23 10:00:00', 1, 17, '', NULL, NULL, NULL, 'blah'
5, 1, 54, '2017-03-23 10:00:00', 1, 34, '', NULL, NULL, NULL, 'blah'
5, 2, 64, '2017-03-23 10:00:00', 1, 17, '', NULL, NULL, NULL, 'blah'
5, 2, 64, '2017-03-23 10:00:00', 1, 34, '', NULL, NULL, NULL, 'blah'
5, 3, 89, '2017-04-08 09:00:00', 1, 25, 'Y', NULL, NULL, NULL, 'blah'
5, 3, 89, '2017-04-08 09:00:00', 1, 16, 'Y', NULL, NULL, NULL, 'blah'
5, 3, 89, '2017-04-08 09:00:00', 1, 76, 'Y', NULL, NULL, NULL, 'blah'
5, 3, 89, '2017-04-08 09:00:00', 1, 82, 'Y', NULL, NULL, NULL, 'blah'
5, 4, 99, '2017-04-08 09:00:00', 1, 25, 'Y', NULL, NULL, NULL, 'blah'
5, 4, 99, '2017-04-08 09:00:00', 1, 16, 'Y', NULL, NULL, NULL, 'blah'
5, 4, 99, '2017-04-08 09:00:00', 1, 76, 'Y', NULL, NULL, NULL, 'blah'
5, 4, 99, '2017-04-08 09:00:00', 1, 82, 'Y', NULL, NULL, NULL, 'blah'
5, 5, 32, '2017-04-14 16:00:00', 3, 32, '', 32, NULL, NULL, 'blah'
5, 5, 32, '2017-04-14 16:00:00', 3, 32, '', 47, NULL, NULL, 'blah'
5, 5, 32, '2017-04-14 16:00:00', 3, 47, '', 32, NULL, NULL, 'blah'
5, 5, 32, '2017-04-14 16:00:00', 3, 47, '', 47, NULL, NULL, 'blah'
The main idea is taken from Partition Function COUNT() OVER possible using DISTINCT that #Jayvee pointed out with a small addition that would make it work when acID has NULL values.
Most likely you can remove all indexes from your #Temp table, the server will have to sort it in several different ways for different window functions anyway, but there is no self-join, so it should be faster.
The plan will have many sorts and they also can be slow, especially when engine underestimates the number of rows in a table. And table variable is exactly this case. Optimiser thinks that table variable has only 1 row. So, I'd recommend to use a classic #Temp table here, even without indexes.
An index on (aiID, acID) should help, but there will be other sorts any way.
WITH
CTE_Counts
AS
(
SELECT
*
-- use DENSE_RANK() to calculate COUNT(DISTINCT)
, DENSE_RANK() OVER (PARTITION BY [aiID] ORDER BY [acID])
+ DENSE_RANK() OVER (PARTITION BY [aiID] ORDER BY [acID] DESC)
-- subtract extra 1 if acID has NULL values within the partition
- MAX(CASE WHEN [acID] IS NULL THEN 1 ELSE 0 END) OVER (PARTITION BY [aiID])
- 1 AS [noac]
FROM #Temp
)
,CTE_SetNum
AS
(
SELECT
*
, DENSE_RANK() OVER (ORDER BY [aisdt], [aiID]) AS [SetNum]
FROM CTE_Counts
WHERE [noac] < [asm]
)
SELECT
*
, MAX([SetNum]) OVER () AS [MaxSet]
FROM CTE_SetNum
ORDER BY
[aisdt]
,[aiID]
,[SetNum]
;
Index as suggested in the comments would definitely play a major part but also I think you can re-write the query without self join in this way:
SELECT MAX([t].[SetNum]) OVER (PARTITION BY NULL) AS [MaxSet]
,*
FROM (
SELECT DENSE_RANK() OVER (ORDER BY [t1].[aisdt], [t1].[aiID]) AS [SetNum]
,[t1].*
FROM (
SELECT * ,dense_rank() over(partition by aiID order by [acID]) -
dense_rank() over(partition by aiID order by [acID]) - 1 AS [noac]
FROM #Temp
) [t1]
WHERE [t1].[noac] < [t1].[asm]
) [t]

SQL Query joining 5 tables

Trying to create a query to select all jobs that are unpaid and who the customer is for that job.
So the is required to first work out a calculation of what they owe (by a sum of s_partorders qty x price found in s_parts) and then minus what they have paid from s_payments.
This query joins it all together but I don't know how to group it by jobNumber because there can be multiple payments and multiple part orders.
SELECT a.jobNumber, a.customerID, a.quoteStatus, a.costDelivery, a.costCallout, a.costLabour, b.customerID, b.firstName, b.lastName, c.paymentID, c.jobNumber, c.amount, d.orderID, d.jobNumber, d.partID, d.quantity, e.partID, e.sellPrice
FROM s_jobcards a
INNER JOIN s_customers b ON a.customerID = b.customerID
INNER JOIN s_payments c ON a.jobNumber = c.jobNumber
INNER JOIN s_partOrders d ON a.jobNumber = d.jobNumber
INNER JOIN s_parts e ON d.partID = e.partID
WHERE a.quoteStatus = 0
Sorry it's quite messy and incomplete...
Included table structure and some test data.
CREATE TABLE IF NOT EXISTS `s_customers` (
`customerID` int(20) NOT NULL AUTO_INCREMENT,
`firstName` text NOT NULL,
`lastName` text NOT NULL,
`address` text NOT NULL,
`suburb` text NOT NULL,
`state` text NOT NULL,
`postcode` text NOT NULL,
`phone` text NOT NULL,
`altPhone` text NOT NULL,
`email` text NOT NULL,
`notes` text NOT NULL,
`postAddress` text NOT NULL,
`serviceDueDate` date NOT NULL,
PRIMARY KEY (`customerID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `s_customers` (`customerID`, `firstName`, `lastName`, `address`, `suburb`, `state`, `postcode`, `phone`, `altPhone`, `email`, `notes`, `postAddress`, `serviceDueDate`) VALUES
(1, 'David', 'Davinci', '654 Fake Road', 'Canning Vale', 'WA', '6164', '9546446', '45645646', 'dave#website.com', 'This guy is a butt', 'Cockburn Central', '2014-12-24'),
(2, 'Timmy', 'Trumpet', '69 something Street', 'Cockburn Central', 'WA', '6164', '9456456', '92344643', 'timmy#trumpet.com', 'Timmah?', '45 Timmy Street', '0000-00-00'),
(3, 'Jerry', 'Tester', '', '', '', '', '', '', '', '', '', '0000-00-00');
CREATE TABLE IF NOT EXISTS `s_jobcards` (
`jobNumber` int(6) NOT NULL AUTO_INCREMENT,
`dateReceived` date NOT NULL,
`workRequired` text NOT NULL,
`workCompleted` text NOT NULL,
`dateCompleted` date NOT NULL,
`customerID` int(5) NOT NULL,
`serviceTime` int(5) NOT NULL,
`serviceTech` int(1) NOT NULL,
`workOutstanding` text NOT NULL,
`quoteStatus` tinyint(1) NOT NULL DEFAULT '1',
`costDelivery` double NOT NULL,
`costCallout` double NOT NULL,
`costLabour` double NOT NULL,
PRIMARY KEY (`jobNumber`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=6 ;
INSERT INTO `s_jobcards` (`jobNumber`, `dateReceived`, `workRequired`, `workCompleted`, `dateCompleted`, `customerID`, `serviceTime`, `serviceTech`, `workOutstanding`, `quoteStatus`, `costDelivery`, `costCallout`, `costLabour`) VALUES
(1, '2013-11-18', 'Create new service software. Yeah! 4534', 'Not a whole lot yet.?', '0000-00-00', 1, 5, 2, 'Complete this software?', 0, 50, 90, 90),
(2, '2013-11-18', 'work required', 'work done!', '0000-00-00', 1, 1, 3, 'work outstanding', 0, 0, 0, 0),
(3, '2014-12-01', 'Work harder.23432432 gdf', 'Go go!', '2014-12-01', 2, 1, 3, '', 1, 0, 0, 0),
(4, '0000-00-00', 'Whack some moles.', '', '0000-00-00', 3, 0, 1, '', 1, 0, 0, 0),
(5, '0000-00-00', '', '', '0000-00-00', 1, 0, 0, '', 1, 0, 0, 0);
CREATE TABLE IF NOT EXISTS `s_partOrders` (
`orderID` int(11) NOT NULL AUTO_INCREMENT,
`jobNumber` int(11) NOT NULL,
`partID` int(11) NOT NULL,
`quantity` int(11) NOT NULL,
PRIMARY KEY (`orderID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `s_partOrders` (`orderID`, `jobNumber`, `partID`, `quantity`) VALUES
(2, 0, 1, 5),
(3, 1, 1, 2),
(4, 1, 1, 6);
CREATE TABLE IF NOT EXISTS `s_parts` (
`partID` int(10) NOT NULL AUTO_INCREMENT,
`partNumber` varchar(50) NOT NULL,
`partDescription` text NOT NULL,
`modelID` int(5) NOT NULL,
`buyPrice` double NOT NULL,
`sellPrice` double NOT NULL,
`notes` text NOT NULL,
PRIMARY KEY (`partID`),
UNIQUE KEY `partNumber` (`partNumber`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=2 ;
INSERT INTO `s_parts` (`partID`, `partNumber`, `partDescription`, `modelID`, `buyPrice`, `sellPrice`, `notes`) VALUES
(1, '3453453453', 'Test Part', 1, 10.02, 30.5, 'This is a test part.');
CREATE TABLE IF NOT EXISTS `s_payments` (
`paymentID` int(11) NOT NULL AUTO_INCREMENT,
`amount` double NOT NULL,
`type` text NOT NULL,
`jobNumber` int(11) NOT NULL,
`paymentDate` date NOT NULL,
PRIMARY KEY (`paymentID`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `s_payments` (`paymentID`, `amount`, `type`, `jobNumber`, `paymentDate`) VALUES
(2, 200, 'Visa', 1, '2014-12-05'),
(3, 20, 'Visa', 1, '2014-12-05');
Use outer joins where there might be no matching records, and aggregating data to jobnumber before joining will assist in ensuring the numbers are accurate:
select
j.jobNumber, j.customerID, j.quoteStatus, j.costDelivery, j.costCallout, j.costLabour
, c.customerID, c.firstName, c.lastName
, p.parts_sellprice
, sum(jp.amount) as paid
from s_jobcards as j
inner join s_customers as c on j.customerID = c.customerID
left outer join s_payments as jp on j.jobNumber = jp.jobNumber
left outer join (
select
d.jobNumber, sum(d.quantity * e.sellPrice) parts_sellprice
from s_partOrders d
left outer join s_parts e ON d.partID = e.partID
group by
d.jobNumber
) as p on j.jobNumber = p.jobNumber
group by
j.jobNumber, j.customerID, j.quoteStatus, j.costDelivery, j.costCallout, j.costLabour
, c.customerID, c.firstName, c.lastName
;
nb: I have assumed the sell price is multiplied by quantity
see this sqlfiddle demo: http://sqlfiddle.com/#!2/96f4c/1
select
j.jobNumber, j.customerID, j.quoteStatus, j.costDelivery, j.costCallout, j.costLabour
, c.customerID, c.firstName, c.lastName
, p.parts_sellprice
, sum(jp.amount) as paid
, (j.costDelivery + j.costCallout + j.costLabour + p.parts_sellprice) as Total_Cost
, (j.costDelivery + j.costCallout + j.costLabour + p.parts_sellprice) - sum(jp.amount) as Amount_Outstanding
from s_jobcards as j
inner join s_customers as c on j.customerID = c.customerID
left outer join s_payments as jp on j.jobNumber = jp.jobNumber
left outer join (
select
d.jobNumber, sum(d.quantity * e.sellPrice) parts_sellprice
from s_partOrders d
left outer join s_parts e ON d.partID = e.partID
group by
d.jobNumber
) as p on j.jobNumber = p.jobNumber
group by
j.jobNumber, j.customerID, j.quoteStatus, j.costDelivery, j.costCallout, j.costLabour
, c.customerID, c.firstName, c.lastName
;