How Can i subtract two columns in different tables in SQL - sql

I want to enter in a query the subtract between two columns in different tables it keeps saying error ...
SELECT FlightDate,
Plane,
Destination,
Capacity
FROM
FlightSchedule,
Routes,
Aircrafts
WHERE
FlightSchedule.RID=Routes.RouteID
AND FlightSchedule.Plane=Aircrafts.AcID
AND (SELECT SUM(Capacity)
FROM Aircrafts)
-
(SELECT Class , count(*)
FROM Tickets);

I would split this up into 2 parts. First get the data you need, then perform the math. You are also using old SQL syntax. You should re-format using the new JOIN syntax. It's also easier to read.
1st declare a table to hold you flight schedule and aircraft info.
Declare #AIRCRAFTCAPACITY Table
(
[FlightDate] [datetime] NOT NULL,
[Plane] [varchar](50) NOT NULL,
[Destination] [varchar](50) NOT NULL,
[Capacity] [INT] NOT NULL,
[NoOfSeatsRemaining] [INT] NULL
);
Then insert the data you need.
INSERT #AIRCRAFTCAPACITY
(
[FlightDate],
[Plane],
[Destination],
[Capacity]
)
SELECT FS.FlightDate,
A.Plane,
FS.Destination,
A.Capacity
FROM
FlightSchedule FS
INNER
JOIN Routes R
ON
FS.RID = R.RouteID
INNER
JOIN Aircrafts A
ON
FS.Plane = A.AcID
Now perform the math to calculate the remaining capacity. I've made an assumption that you are doing this for a particular route. But I'm sure you can adjust your SQL accordingly.
UPDATE #AIRCRAFTCAPACITY
SET
[NoOfSeatsRemaining] = [Capacity] - T.TICKETS_SOLD
FROM
#AIRCRAFTCAPACITY A
INNER
JOIN
(
SELECT ROUTEID, COUNT(ROUTEID) AS TICKETS_SOLD
FROM
Tickets T1
WHERE
T1.ROUTEID = A.ROUTEID
GROUP
BY ROUTEID
) T
ON
A.ROUTEID = T.ROUTEID
Apolgies if the syntax is a little off as it's hard to construct SQL when you dont have the underlying tables.
But hopefully it will help.

The query is wrong on multiple levels.
First of all, you cannot put the details of SELECT capacity as a sub query under WHERE. The WHERE clause is for conditional parameters.
Try this:
WITH VACANCY as (SELECT SUM(Capacity) FROM Aircrafts) - (SELECT Count(*) FROM Tickets)
SELECT FlightDate,Plane, Destination, Vacancy
FROM FlightSchedule,Routes,Aircrafts
WHERE FlightSchedule.RID=Routes.RouteID AND FlightSchedule.Plane=Aircrafts.AcID;
You also need to a condition for the select count for tickets but I don't know the schema of tickets so...

Related

Returns all values in 3 tables

I have three tables:
CREATE TABLE [dbo].[Data]
(
[PorID] [int] NOT NULL,
[HourS] [int] NOT NULL
)
CREATE TABLE [dbo].[TimeData]
(
[HId] [bigint] NOT NULL,
[HName] [varchar](50) NOT NULL,
[HHour] [int] NOT NULL
)
CREATE TABLE [dbo].PortInfo
(
[Id] [bigint] NOT NULL,
[PortName] [varchar](50) NOT NULL
)
Even if the port is not present in the Data table it should return rows for all port in PortInfo table. Similarly, it should always return 24 records for each port. The result should display all ports for each record even if doesn't exist within the Data table.
Updated Answer
Based on what you are telling me in the comments and me filling in some blanks, this is what I assume you are looking for. This will produce a record for every hour, for every port.
SELECT
td.HHour,
td.HName,
pi.Id,
pi.PortName,
d.PorID,
d.HourS
FROM
dbo.TimeData td
FULL OUTER JOIN
dbo.PortInfo pi
ON (1 = 1)
LEFT OUTER JOIN
dbo.Data d
ON (d.PorID = pi.Id)
AND (d.HourS = td.HHour)
Output:
Some feedback to make this process easier. Share your schema (relationships) and/or some sample data. Also, consider creating more logical/intuitive names for your columns so that relationships and content may be implied.
Original Answer
This sounds like what you are looking for. The query below will return all port information (from PortInfo) even if its Id is not in the Data table (PorId). This is done by using a LEFT JOIN onto the PortInfo table.
SELECT
po.Id,
po.PortName,
d.HourS
FROM
dbo.PortInfo po
LEFT JOIN
dbo.Data d
ON (d.PorID = po.Id)
Now, you don't mention the how or if the 3rd table TimeData should be used, but if you wanted that information in your result as well, you can simply LEFT JOIN that as well:
SELECT
po.Id,
po.PortName,
d.HourS,
td.HName,
td.HHour
FROM
dbo.PortInfo po
LEFT JOIN
dbo.Data d
ON (d.PorID = po.Id)
LEFT JOIN
dbo.TimeData td
ON (td.HId = d.HourS) -- I assume this is the link, you may need to update if not.

T-SQL - get count of joined entries

I wonder how better to write the following query to Microsoft SQL Server.
I have three tables: surveys, survey_presets and survey_scenes. They have the following columns:
CREATE TABLE [dbo].[surveys](
[id] [int] IDENTITY(1,1) NOT NULL,
[caption] [nvarchar](255) NOT NULL,
[creation_time] [datetime] NOT NULL,
)
CREATE TABLE [dbo].[survey_presets](
[id] [int] IDENTITY(1,1) NOT NULL,
[survey_id] [int] NOT NULL,
[preset_id] [int] NOT NULL,
)
CREATE TABLE [dbo].[survey_scenes](
[id] [int] IDENTITY(1,1) NOT NULL,
[survey_id] [int] NOT NULL,
[scene_id] [int] NOT NULL,
)
Both survey_presets and survey_scenes have foreign keys on surveys for survey_id column.
Now I want to select all surveys with the count of corresponding presets and scenes for each. Here is the "pseudo-query" of what I want:
SELECT
surveys.*,
COUNT(survey_presets, where survey_presets.survey_id = surveys.id),
COUNT(survey_scenes, where survey_scenes.survey_id = surveys.id)
FROM surveys
ORDER BY suverys.creation_time
I can do a mess with SELECT DISTINCT, JOIN, GROUP BY, etc., but I'm new to T-SQL and I doubt my query will be optimal in any sense.
I would do the counting in subqueries to avoid cartesian products. As you might have a few matching rows in presets and also a few in scenes resulting count might be multiplied. You might write simple join query and avoid the multiplication by counting distinct survey_presets.id and distinct survey_scenes.id though.
SELECT
surveys.*,
isnull(presets_count, 0) presets_count,
isnull(scenes_count, 0) scenes_count
FROM surveys
LEFT JOIN
(
SELECT survey_id,
count(*) presets_count
FROM survey_presets
GROUP BY survey_id
) presets
ON surveys.id = presets.survey_id
LEFT JOIN
(
SELECT survey_id,
count(*) scenes_count
FROM survey_scenes
GROUP BY survey_id
) scenes
ON surveys.id = scenes.survey_id
ORDER BY surveys.creation_time
How it works
You can introduce a special kind of subquery called derived table to FROM section of your query. Derived table is defined as normal query enclosed in parenthesis and followed by table alias. It cannot use any column from outer query, but can expose columns you use in ON section to join derived table to main body of the query.
In this case derived table simply count rows grouped by id; joins connect the counts to surveys.
SELECT surveys.ID, surveys.caption, surveys.creation_time,
count(survey_presets.survey_id) as survey_presets,
count(survey_scenes.survey_id) as survey_scenes
FROM surveys
LEFT OUTER JOIN survey_presets on survey_presets.survey_id = surveys.id
LEFT OUTER JOIN survey_scenes on survey_scenes.survey_id = surveys.id
GROUP BY surveys.ID, surveys.caption, surveys.creation_time
ORDER BY suverys.creation_time

SQL joins with multiple records into one with a default

My 'people' table has one row per person, and that person has a division (not unique) and a company (not unique).
I need to join people to p_features, c_features, d_features on:
people.person=p_features.num_value
people.division=d_features.num_value
people.company=c_features.num_value
... in a way that if there is a record match in p_features/d_features/c_features only, it would be returned, but if it was in 2 or 3 of the tables, the most specific record would be returned.
From my test data below, for example, query for person=1 would return
'FALSE'
person 3 returns maybe, person 4 returns true, and person 9 returns default
The biggest issue is that there are 100 features and I have queries that need to return all of them in one row. My previous attempt was a function which queried on feature,num_value in each table and did a foreach, but 100 features * 4 tables meant 400 reads and it brought the database to a halt it was so slow when I loaded up a few million rows of data.
create table p_features (
num_value int8,
feature varchar(20),
feature_value varchar(128)
);
create table c_features (
num_value int8,
feature varchar(20),
feature_value varchar(128)
);
create table d_features (
num_value int8,
feature varchar(20),
feature_value varchar(128)
);
create table default_features (
feature varchar(20),
feature_value varchar(128)
);
create table people (
person int8 not null,
division int8 not null,
company int8 not null
);
insert into people values (4,5,6);
insert into people values (3,5,6);
insert into people values (1,2,6);
insert into p_features values (4,'WEARING PANTS','TRUE');
insert into c_features values (6,'WEARING PANTS','FALSE');
insert into d_features values (5,'WEARING PANTS','MAYBE');
insert into default_features values('WEARING PANTS','DEFAULT');
You need to transpose the features into rows with a ranking. Here I used a common-table expression. If your database product does not support them, you can use temporary tables to achieve the same effect.
;With RankedFeatures As
(
Select 1 As FeatureRank, P.person, PF.feature, PF.feature_value
From people As P
Join p_features As PF
On PF.num_value = P.person
Union All
Select 2, P.person, PF.feature, PF.feature_value
From people As P
Join d_features As PF
On PF.num_value = P.division
Union All
Select 3, P.person, PF.feature, PF.feature_value
From people As P
Join c_features As PF
On PF.num_value = P.company
Union All
Select 4, P.person, DF.feature, DF.feature_value
From people As P
Cross Join default_features As DF
)
, HighestRankedFeature As
(
Select Min(FeatureRank) As FeatureRank, person
From RankedFeatures
Group By person
)
Select RF.person, RF.FeatureRank, RF.feature, RF.feature_value
From people As P
Join HighestRankedFeature As HRF
On HRF.person = P.person
Join RankedFeatures As RF
On RF.FeatureRank = HRF.FeatureRank
And RF.person = P.person
Order By P.person
I don't know if I had understood very well your question, but to use JOIN, you need your table loaded already and then use the SELECT statement with INNER JOIN, LEFT JOIN or whatever you need to show.
If you post some more information, maybe turn it easy to understand.
There are some aspects of your schema I'm not understanding, like how to relate to the default_features table if there's no match in any of the specific tables. The only possible join condition is on feature, but if there's no match in the other 3 tables, there's no value to join on. So, in my example, I've hard-coded the DEFAULT since I can't think of how else to get it.
Hopefully this can get you started and if you can clarify the model a bit more, the solution can be refined.
select p.person, coalesce(pf.feature_value, df.feature_value, cf.feature_value, 'DEFAULT')
from people p
left join p_features pf
on p.person = pf.num_value
left join d_features df
on p.division = df.num_value
left join c_features cf
on p.company = cf.num_value

Oracle sql query running for (almost) forever

An application of mine is trying to execute a count(*) query which returns after about 30 minutes. What's strange is that the query is very simple and the tables involved are large, but not gigantic (10,000 and 50,000 records).
The query which takes 30 minutes is:
select count(*)
from RECORD r inner join GROUP g
on g.GROUP_ID = r.GROUP_ID
where g.BATCH_ID = 1 and g.ENABLED = 'Y'
The database schema is essentially:
create table BATCH (
BATCH_ID int not null,
[other columns]...,
CONSTRAINT PK_BATCH PRIMARY KEY (BATCH_ID)
);
create table GROUP (
GROUP_ID int not null,
BATCH_ID int,
ENABLED char(1) not null,
[other columns]...,
CONSTRAINT PK_GROUP PRIMARY KEY (GROUP_ID),
CONSTRAINT FK_GROUP_BATCH_ID FOREIGN KEY (BATCH_ID)
REFERENCES BATCH (BATCH_ID),
CONSTRAINT CHK_GROUP_ENABLED CHECK(ENABLED in ('Y', 'N'))
);
create table RECORD (
GROUP_ID int not null,
RECORD_NUMBER int not null,
[other columns]...,
CONSTRAINT PK_RECORD PRIMARY KEY (GROUP_ID, RECORD_NUMBER),
CONSTRAINT FK_RECORD_GROUP_ID FOREIGN KEY (GROUP_ID)
REFERENCES GROUP (GROUP_ID)
);
create index IDX_GROUP_BATCH_ID on GROUP(BATCH_ID);
I checked whether there are any blocks in the database and there are none. I also ran the following pieces of the query and all except the last two returned instantly:
select count(*) from RECORD -- 55,501
select count(*) from GROUP -- 11,693
select count(*)
from RECORD r inner join GROUP g
on g.GROUP_ID = r.GROUP_ID
-- 55,501
select count(*)
from GROUP g
where g.BATCH_ID = 1 and g.ENABLED = 'Y'
-- 3,112
select count(*)
from RECORD r inner join GROUP g
on g.GROUP_ID = r.GROUP_ID
where g.BATCH_ID = 1
-- 27,742 - took around 5 minutes to run
select count(*)
from RECORD r inner join GROUP g
on g.GROUP_ID = r.GROUP_ID
where g.ENABLED = 'Y'
-- 51,749 - took around 5 minutes to run
Can someone explain what's going on? How can I improve the query's performance? Thanks.
A coworker figured out the issue. It's because the table statistics weren't being updated and the last time the table was analyzed was a couple of months ago (when the table was essentially empty). I ran analyze table RECORD compute statistics and now the query is returning in less than a second.
I'll have to talk to the DBA about why the table statistics weren't being updated.
SELECT COUNT(*)
FROM RECORD R
LEFT OUTER JOIN GROUP G ON G.GROUP_ID = R.GROUP_ID
AND G.BATCH_ID = 1
AND G.ENABLED = 'Y'
Try that and let me know how it turns out. Not saying this IS the answer, but since I don't have access to a DB right now, I can't test it. Hope it works for ya.
An explain plan would be a good place to start.
See here:
Strange speed changes with sql query
for how to use the explain plan syntax (and query to see the result.)
If that doesn't show anything suspicious, you'll probably want to look at a trace.

SQL function to sort by most popular content

I don't know if this is possible with SQL:
I have two tables, one of content, each with an integer ID, and a table of comments each with an "On" field denoting the content it is on. I'd like to receive the content in order of how many comments have it in their "On" field, and was hoping SQL could do it.
SELECT comment.on AS content_id, COUNT(comment_id) AS num_comments
FROM comments
GROUP BY content_id
ORDER BY num_comments DESC
If you need all the fields of the content, you can do a join:
SELECT contents.*, COUNT(comment_id) AS num_comments
FROM contents
LEFT JOIN comments on contents.content_id = comments.on
GROUP BY content_id
ORDER BY num_comments DESC
select c.id, count(cmt.*) as cnt
from Content c, Comment cmt
where c.id = cmt.id
order by cnt
group by c.id,
Let's assume your tables look like this (I wrote this in pseudo-SQL - syntax may differ depending on database you are using). From the description you provided, it is not clear how you are joining the tables. Nevertheless, I think it looks something like this (with the caveat that all primary keys, indexes, and so forth are missing):
CREATE TABLE [dbo].[Content] (
[ContentID] [int] NOT NULL,
[ContentText] [varchar](50) NOT NULL
)
CREATE TABLE [dbo].[ContentComments] (
[ContentCommentID] [int] NOT NULL,
[ContentCommentText] [varchar](50) NOT NULL,
[ContentID] [int] NOT NULL
)
ALTER TABLE [dbo].[ContentComments] WITH CHECK ADD CONSTRAINT
[FK_ContentComments_Content] FOREIGN KEY([ContentID])
REFERENCES [dbo].[Content] ([ContentID])
Here is how you would write your query to get the content sorted by the number of comments each piece of content has. The DESC sorts the content items from those with the most comments to those with the least comments.
SELECT Content.ContentID, COUNT(ContentComments.ContentCommentID) AS CommentCount
FROM Content
INNER JOIN ContentComments
ON Content.ContentID = ContentComments.ContentID
GROUP BY Content.ContentID
ORDER BY COUNT(ContentComments.ContentCommentID) DESC