SQL Query - Complex query between tables - sql

I currently have the 5 following tables. Each Site has (1 or Many) Project Managers as well as (1 or Many) Site_Supervisors. A Project Manager / Site Supervisor can be assigned to many sites
I currently have a working query:
SELECT Sites.Site_Name, Sites.Site_Street_Address, Sites.Site_Suburb, Sites.Site_State, Sites.Site_Postcode, Sites.Site_Region, Sites.Completed, Project_Managers.First_Name AS PM_First_Name, Project_Managers.Last_Name AS PM_Last_Name, Site_Supervisors.First_Name AS SS_First_Name, Site_Supervisors.Last_Name AS SS_Last_Name, Sites.Date_Started
FROM Site_Site_Supervisors INNER JOIN
Site_Supervisors ON Site_Site_Supervisors.Site_Supervisor_ID = Site_Supervisors.Site_Supervisor_ID RIGHT OUTER JOIN
Sites ON Site_Site_Supervisors.Site_ID = Sites.Site_ID LEFT OUTER JOIN
Project_Managers INNER JOIN
Site_Project_Managers ON Project_Managers.Project_Manager_ID = Site_Project_Managers.Project_Manager_ID ON
Sites.Site_ID = Site_Project_Managers.Site_ID
WHERE (Sites.Completed = 0)
ORDER BY Sites.Site_Name
Which gives me the desired output, which i then work with in my code.
I am wanting to make a change to the query, where instead of outputting the:
Project_Managers.First_Name AS PM_First_Name
Project_Managers.Last_Name AS PM_Last_Name
instead i replace these with:
Project_Managers.First_Name where the corresponding Site_Project_Managers.Primary_Contact = True AS PM_First_Name
Project_Managers.Last_Name where the corresponding Site_Project_Managers.Primary_Contact = True AS PM_Last_Name
Project_Managers.First_Name where the corresponding Site_Project_Managers.Primary_Contact = False AS AssistantPM_First_Name
Project_Managers.Last_Name where the corresponding Site_Project_Managers.Primary_Contact = False AS AssistantPM_Last_Name
To perform this query is beyond my SQL skillset at this time, so i am hoping one of you can provide me with the correct query to use and or some guidance
Thanks
Sites
[Site_ID] [int] IDENTITY(1,1) NOT NULL (PK),
[Site_Name] [varchar](50) NOT NULL,
[Site_Street_Address] [varchar](50) NULL,
[Site_Suburb] [varchar](50) NULL,
[Site_State] [varchar](50) NULL,
[Site_Postcode] [varchar](10) NULL,
[Site_Region] [varchar](50) NULL,
[Date_Started] [datetime] NULL,
[Completed] [bit] NOT NULL
Project_Managers
[Project_Manager_ID] [int] IDENTITY(1,1) NOT NULL (PK),
[First_Name] [varchar](50) NOT NULL,
[Last_Name] [varchar](50) NOT NULL,
[Phone_Number] [varchar](50) NULL,
[Email_Address] [varchar](50) NULL,
[Currently_Employed] [bit] NOT NULL,
CONSTRAINT [PK_Project_Managers] PRIMARY KEY CLUSTERED
Site_Supervisors
[Site_Supervisor_ID] [int] IDENTITY(1,1) NOT NULL (PK),
[First_Name] [varchar](50) NOT NULL,
[Last_Name] [varchar](50) NOT NULL,
[Phone_Number] [varchar](50) NULL,
[Email_Address] [varchar](50) NULL,
[Currently_Employed] [bit] NOT NULL,
CONSTRAINT [PK_Site_Supervisors_1] PRIMARY KEY CLUSTERED
Site_Project_Managers
[Site_ID] [int] NOT NULL (PK),
[Project_Manager_ID] [int] NOT NULL (PK),
[Primary_Contact] [bit] NULL,
[Alerts] [bit] NULL,
CONSTRAINT [PK_Site_Project_Managers] PRIMARY KEY CLUSTERED
Site_Site_Supervisors
[Site_ID] [int] NOT NULL (PK),
[Site_Supervisor_ID] [int] NOT NULL (PK),
[Primary_Contact] [bit] NULL,
[Alerts] [bit] NULL,
CONSTRAINT [PK_Site_Site_Supervisors] PRIMARY KEY CLUSTERED

Assumed you are using MySql and you may use the CASE or inline IF ELSE to get the desired output that you want
SELECT Sites.Site_Name,
Sites.Site_Street_Address,
Sites.Site_Suburb,
Sites.Site_State,
Sites.Site_Postcode,
Sites.Site_Region,
Sites.Completed,
CASE Site_Project_Managers.Primary_Contact
WHEN true THEN Project_Managers.First_Name
END AS PM_First_Name,
CASE Site_Project_Managers.Primary_Contact
WHEN true THEN Project_Managers.Last_Name
END AS PM_Last_Name,
CASE Site_Project_Managers.Primary_Contact
WHEN false THEN Project_Managers.First_Name
END AS AssistantPM_First_Name,
CASE Site_Project_Managers.Primary_Contact
WHEN false THEN Project_Managers.Last_Name
END AS AssistantPM_Last_Name,
Site_Supervisors.First_Name AS SS_First_Name,
Site_Supervisors.Last_Name AS SS_Last_Name,
Sites.Date_Started
FROM Site_Site_Supervisors
INNER JOIN Site_Supervisors
ON Site_Site_Supervisors.Site_Supervisor_ID = Site_Supervisors.Site_Supervisor_ID
RIGHT OUTER JOIN Sites
ON Site_Site_Supervisors.Site_ID = Sites.Site_ID
LEFT OUTER JOIN Project_Managers
INNER JOIN Site_Project_Managers
ON Project_Managers.Project_Manager_ID = Site_Project_Managers.Project_Manager_ID
ON Sites.Site_ID = Site_Project_Managers.Site_ID
WHERE (Sites.Completed = 0)
ORDER BY Sites.Site_Name

Related

how can I improve performance for this query

I have been battling for ages on how to complete a data task that I have, I have tried doing in c# as I am proficient at this, but its was taking forever!!! so I have decided to do in SQL Server, however it looks like it is still going to take forever, maybe as long as 20 days to complete.
Does anyone know any more efficient ways to write my stored procedure?
ALTER PROCEDURE [dbo].[TotalWins]
AS
BEGIN
SET NOCOUNT ON
DECLARE #meeting_date DATE;
DECLARE #idStore INT;
DECLARE #race_idStore INT;
DECLARE #runner_id INT;
SET #idStore = 0;
SET #race_idStore = -1;
SET #runner_id = 0;
WHILE(#idStore IS NOT NULL)
BEGIN
SET #race_idStore = -1;
SET #runner_id = 0;
SELECT
#idStore = MIN(runners.id)
FROM
dbHorseRacing.dbo.historic_runners AS runners
WHERE
runners.id > #idStore;
IF #idStore IS NOT NULL
BEGIN
SELECT
#runner_id = runners.runner_id, #meeting_date = races.meeting_date
FROM
dbHorseRacing.dbo.historic_runners AS runners
INNER JOIN
dbHorseRacing.dbo.historic_races AS races ON races.race_id = runners.race_id
WHERE
runners.id > #idStore;
INSERT INTO dbHorseRacing.dbo.total_wins
SELECT
#idStore, COUNT(*) AS total_wins
FROM
dbHorseRacing.dbo.historic_runners AS runners
INNER JOIN
dbHorseRacing.dbo.historic_races AS races ON races.race_id = runners.race_id
WHERE
runners.runner_id = #runner_id
AND races.meeting_date < #meeting_date
AND runners.finish_position = 1;
END
END
END
I am updating this with question with the ddl and a data sample for the races and runners table. Sorry that they are quite large...
race sample date:
race_id meeting_id meeting_date course conditions race_name race_abbrev_name race_type_id race_type race_num going direction class draw_advantage num_fences handicap all_weather seller claimer apprentice maiden amateur num_runners num_finishers rating group_race min_age max_age distance_yards added_money official_rating speed_rating private_handicap scheduled_time off_time winning_time_disp winning_time_secs standard_time_disp standard_time_secs loaded_at
-1 2941 2003-07-03 Newbury Arab Race The Emirates Arabian International Arab Race 12 Flat 1 Good Left Handed 1 High numbers best in large fields, especially in very soft ground. NULL 0 0 0 0 0 0 0 8 8 NULL NULL NULL NULL 1320 0 NULL NULL NULL 2003-07-03 18:10:00.000 2003-07-03 00:00:00.000 0:00.00 0 1:14.38 74.379997253418 0x00000000000007DB
race ddl:
[dbo].[historic_races]
(
[race_id] [int] NOT NULL,
[meeting_id] [int] NOT NULL,
[meeting_date] [date] NOT NULL,
[course] [varchar](255) NOT NULL,
[conditions] [varchar](255) NOT NULL,
[race_name] [varchar](255) NOT NULL,
[race_abbrev_name] [varchar](80) NOT NULL,
[race_type_id] [int] NOT NULL,
[race_type] [varchar](80) NOT NULL,
[race_num] [tinyint] NOT NULL,
[going] [varchar](80) NULL,
[direction] [varchar](80) NULL,
[class] [tinyint] NULL,
[draw_advantage] [varchar](255) NULL,
[num_fences] [tinyint] NULL,
[handicap] [tinyint] NULL,
[all_weather] [tinyint] NULL,
[seller] [tinyint] NULL,
[claimer] [tinyint] NULL,
[apprentice] [tinyint] NULL,
[maiden] [tinyint] NULL,
[amateur] [tinyint] NULL,
[num_runners] [tinyint] NULL,
[num_finishers] [tinyint] NULL,
[rating] [int] NULL,
[group_race] [int] NULL,
[min_age] [tinyint] NULL,
[max_age] [tinyint] NULL,
[distance_yards] [int] NULL,
[added_money] [float] NULL,
[official_rating] [int] NULL,
[speed_rating] [int] NULL,
[private_handicap] [int] NULL,
[scheduled_time] [datetime] NULL,
[off_time] [datetime] NULL,
[winning_time_disp] [varchar](20) NULL,
[winning_time_secs] [float] NULL,
[standard_time_disp] [varchar](20) NULL,
[standard_time_secs] [float] NULL,
[loaded_at] [timestamp] NULL,
PRIMARY KEY CLUSTERED
(
[race_id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
Sample date for runners:
runner_id race_id name foaling_date colour distance_travelled form_figures gender age bred cloth_number stall_number num_fences_jumped long_handicap how_easy_won in_race_comment official_rating official_rating_type speed_rating speed_rating_type private_handicap private_handicap_type trainer_name trainer_id owner_name owner_id jockey_name jockey_id jockey_claim dam_name dam_id sire_name sire_id dam_sire_name dam_sire_id forecast_price forecast_price_decimal starting_price starting_price_decimal betting_text position_in_betting finish_position amended_position unfinished distance_beaten distance_won distance_behind_winner prize_money tote_win tote_place days_since_ran last_race_type_id last_race_type last_race_beaten_fav weight_pounds penalty_weight over_weight tack_hood tack_visor tack_blinkers tack_eye_shield tack_eye_cover tack_cheek_piece tack_pacifiers tack_tongue_strap id total_wins
1 82 401251 David Jack 2010-03-21 CH 143 NULL C 2 UK 4 3 NULL NULL NULL slowly into stride, took keen hold and soon in touch, pushed along and kept on same pace inside final furlong NULL NULL 32 Flat 18 Flat B J Meehan 9262 Roldvale Limited 2311 T E Durcan 18761 NULL NULL NULL NULL NULL NULL NULL 8/1 9 9/1 10 op 8/1 tchd 10/1 5 4 NULL NULL 2 NULL 3.5 216.449996948242 NULL NULL NULL NULL NULL NULL 129 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 1267937 NULL
runners ddl:
[dbo].[historic_runners]
(
[runner_id] [int] NOT NULL,
[race_id] [int] NOT NULL,
[name] [varchar](255) NOT NULL,
[foaling_date] [date] NULL,
[colour] [varchar](20) NOT NULL,
[distance_travelled] [int] NULL,
[form_figures] [varchar](80) NULL,
[gender] [varchar](20) NULL,
[age] [int] NULL,
[bred] [varchar](4) NULL,
[cloth_number] [int] NULL,
[stall_number] [int] NULL,
[num_fences_jumped] [int] NULL,
[long_handicap] [int] NULL,
[how_easy_won] [int] NULL,
[in_race_comment] [text] NULL,
[official_rating] [int] NULL,
[official_rating_type] [varchar](80) NULL,
[speed_rating] [int] NULL,
[speed_rating_type] [varchar](80) NULL,
[private_handicap] [int] NULL,
[private_handicap_type] [varchar](80) NULL,
[trainer_name] [varchar](80) NULL,
[trainer_id] [int] NULL,
[owner_name] [varchar](255) NULL,
[owner_id] [int] NULL,
[jockey_name] [varchar](80) NULL,
[jockey_id] [int] NULL,
[jockey_claim] [int] NULL,
[dam_name] [varchar](80) NULL,
[dam_id] [int] NULL,
[sire_name] [varchar](80) NULL,
[sire_id] [int] NULL,
[dam_sire_name] [varchar](80) NULL,
[dam_sire_id] [int] NULL,
[forecast_price] [varchar](20) NULL,
[forecast_price_decimal] [float] NULL,
[starting_price] [varchar](20) NULL,
[starting_price_decimal] [float] NULL,
[betting_text] [text] NULL,
[position_in_betting] [int] NULL,
[finish_position] [int] NULL,
[amended_position] [int] NULL,
[unfinished] [varchar](30) NULL,
[distance_beaten] [float] NULL,
[distance_won] [float] NULL,
[distance_behind_winner] [float] NULL,
[prize_money] [float] NULL,
[tote_win] [float] NULL,
[tote_place] [float] NULL,
[days_since_ran] [int] NULL,
[last_race_type_id] [int] NULL,
[last_race_type] [varchar](80) NULL,
[last_race_beaten_fav] [int] NULL,
[weight_pounds] [int] NULL,
[penalty_weight] [int] NULL,
[over_weight] [int] NULL,
[tack_hood] [int] NULL,
[tack_visor] [int] NULL,
[tack_blinkers] [int] NULL,
[tack_eye_shield] [int] NULL,
[tack_eye_cover] [int] NULL,
[tack_cheek_piece] [int] NULL,
[tack_pacifiers] [int] NULL,
[tack_tongue_strap] [int] NULL,
[id] [int] NOT NULL,
[total_wins] [int] NULL,
CONSTRAINT [PK_RunnerRaceID] PRIMARY KEY CLUSTERED
(
[runner_id] ASC,
[race_id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Desired results - total_wins table
[dbo].[total_wins]
(
[id] [int] NOT NULL,
[total_wins] [int] NULL
)
The "id" on the total wins table corresponds to the id of the runners table, so I have 2mill rows in the runners table, with a unique indicator called id (not to be confused with the runner_id column, which contains duplicate entries as 1 runner can run in lots of races). So I would hope to end up with 2 million rows in the total_wins table, the total wins reflects how many races, the runner has won,prior to the date of the particular race the row relates to.
Any help would be really appreciated!! I have been struggling with this, I even considered flattening down the data and using a big data solution like hadoop, or mongodb.
Thanks
Laura
Thanks to Davids suggestion about using the group by, and avoiding the loop through, I think this is the potential solution...
SELECT runners.id, count(*) as total_wins
FROM dbo.historic_runners as runners
inner join dbo.historic_races as races on races.race_id = runners.race_id
where races.meeting_date <
(
select meeting_date
FROM dbo.historic_runners as ru
inner join dbo.historic_races as ra on ra.race_id = ru.race_id
where ru.id = runners.id
)
and runners.finish_position = 1
group by runners.id
Thanks for you answers on this problem, I appreciate it :)
Laura, I do not know the exact properties of your database, so I will only give you general ideas of what could be improved.
Detecting what is slow
You will need to test what is slow. Make a copy of your database and try to run the queries without the inserts. And then try to run a lot of inserts without custom selections. This way you will detect whether the writes or the reads are slow in your case. If none of these makes it slow, then something else happens to the table slowing down your process.
Take a look at the schema
Take a look whether the schema is adequate, like whether your database is in normal form and if so, in which one. If it is not in normal form, it might be a good idea to convert it to normal form.
Indexing
Take a look at indexes. If reads are slow, then you will need to add indexes for the columns which are involved in your query, but make sure you read an article before you do that about indexing if you are not well versed in the area. If writes are slow, then consider removing unnecessary indexes, like those involving columns which are not used in queries.
Larger batches
I understand that you are iterating the set by each user, but there are probably not so many races that you need to iterate them one-by-one. You could iterate them by batches of 100, by first getting the minimum, then getting the maximum in a select top 100 query ordering by runners.id. This will probably quicken up your process. Note that in later steps you will put the maximum into the minimum, so you will need only one query to determine limits after the very first iteration.
Last but not least
If writes are slow, then you can make a copy of the main table with a lot of indexes, so everything will be quick there and copy only relevant subsets there periodically and use that as a source for the stored procedure, so queries will be quick. This would increase performance, but avoid it if you are not forced to do it, since this adds a lot of maintenance work and a lot of additional possibilities to err.

SQL Server partition and index

I have a requirement to design a table that is going to have around 80 million records. I created a partition for every month using persisted column (if its wrong suggest me the best way). Please find below scripts that I used to create tables and partition and the query that's going to be used often. Only Insertion and deletion will be done on this table.
-- Create the Partition Function
CREATE PARTITION FUNCTION PF_Invoice_item (int)
AS RANGE LEFT FOR VALUES (1,2,3,4,5,6,7,8,9,10,11,12);
-- Create the Partition Scheme
CREATE PARTITION SCHEME PS_Invoice_item
AS PARTITION PF_Invoice_item ALL TO ([Primary]);
CREATE TABLE [Invoice]
(
[invoice_id] [bigint] NOT NULL,
[Invoice_Number] [varchar](255) NULL,
[Invoice_Date] [date] NULL,
[Invoice_Total] [numeric](18, 2) NULL,
[Outstanding_Balance] [decimal](18, 2) NULL,
CONSTRAINT [PK_Invoice_id] PRIMARY KEY CLUSTERED([invoice_id] ASC)
)
CREATE TABLE [InvoiceItem](
[invoice_item_id] [bigint] NOT NULL,
[invoice_id] [bigint] NOT NULL,
[invoice_Date] [date] NULL,
[make] [varchar](255) NULL,
[serial_number] [varchar](255) NULL,
[asset_id] [varchar](100) NULL,
[application] [varchar](255) NULL,
[customer] [varchar](255) NULL,
[ucid] [varchar](255) NULL,
[dcn] [varchar](255) NULL,
[dcn_name] [varchar](255) NULL,
[device_serial_number] [varchar](255) NULL,
[subscription_name] [varchar](255) NULL,
[product_name] [varchar](255) NULL,
[subscription_start_date] [date] NULL,
[subscription_end_date] [date] NULL,
[duration] [varchar](50) NULL,
[promo_name] [varchar](255) NULL,
[promo_end_date] [date] NULL,
[discount] [decimal](18, 2) NULL,
[tax] [decimal](18, 2) NULL,
[line_item_total] [decimal](18, 2) NULL,
[mth] AS (datepart(month,[invoice_date])) PERSISTED NOT NULL,**
[RELATED_PRODUCT_RATEPLAN_NAME] [varchar](250) NULL,
[SUB_TOTAL] [decimal](18, 2) NULL,
[BILLING_START_DATE] [date] NULL,`enter code here`
[BILLING_END_DATE] [date] NULL,
[SUBSCRIPTION_ID] [varchar](200) NULL,
[DEVICE_TYPE] [varchar](200) NULL,
[BASE_OR_PROMO] [varchar](200) NULL,
CONSTRAINT [PK_InvoiceItem_ID] PRIMARY KEY CLUSTERED ([invoice_item_id]
ASC,[mth] ASC))
ON PS_Invoice_item(mth);
GO
ALTER TABLE [InvoiceItem] WITH CHECK ADD CONSTRAINT [FK_Invoice_ID]
FOREIGN KEY([invoice_id])
REFERENCES [Invoice] ([invoice_id])
GO
I will be using below queries
select subscription_name,duration,start_date,end_date,promotion_name,
promotion_end_date,sub_total,discount,tax,line_item_total from InvoiceItem
lt inner join Invoice on lt.invoice_id=invoice.invoice_id where
invoice.invoice_number='' and lt.customer='' and lt.ucid='' lt.make='' and
lt.SERIAL_NUMBER='' and lt.dcn='' and lt.application=''
select customer,make,application from billing.AssetApplicationTotals
lineItem inner join billing.Invoice invoice on
lineItem.invoice_id=invoice.invoice_id where invoice.invoice_number='';
SELECT [invoice_Date],[make],[serial_number],[application],[customer],
[ucid],[dcn],[dcn_name],[device_serial_number]
,[subscription_name],[product_name],[subscription_start_date],
[subscription_end_date],[duration],[promo_name],[promo_end_date]
FROM [InvoiceItem] where [application]=''
SELECT [invoice_Date],[make],[serial_number],[application],[customer],
[ucid],[dcn],[dcn_name],[device_serial_number]
,[subscription_name],[product_name],[subscription_start_date],
[subscription_end_date],[duration],[promo_name],[promo_end_date]
FROM [InvoiceItem] where [customer]=''
What is the best way to create index? Shall I create separate non clustered index for each filter, or shall I have Composite index and shall I have covering index to avoid key lookup?

SQL query that can display data with a null ID and with not null id on the same Datatable?

Hello I have a question here...
I'll show you my tables first...
[Vendedores](
[IdVendedor] [int] IDENTITY(1,1) NOT NULL,
[IdGrupo] [int] NULL,
[IdInfoContacto] [int] NULL,
[ApellidoPaterno] [varchar](30) NULL,
[ApellidoMaterno] [varchar](30) NULL,
[Nombre] [varchar](35) NULL,
[Estado] [varchar](10) NULL,
CONSTRAINT [PK_Vendedores] PRIMARY KEY CLUSTERED
[Clientes](
[IdCliente] [int] IDENTITY(1,1) NOT NULL,
[IdGrupo] [int] NULL,
[IdVendedor] [int] NULL,
[IdDireccion] [int] NULL,
[IdInfoContacto] [int] NULL,
[FechaAlta] [date] NULL,
[ApellidoPaterno] [varchar](30) NULL,
[ApellidoMaterno] [varchar](30) NULL,
[Nombre] [varchar](40) NULL,
[Empresa] [text] NULL,
[Estado] [varchar](20) NULL,
[Estatus] [varchar](20) NULL,
CONSTRAINT [PK_Clientes] PRIMARY KEY CLUSTERED
[dbo].[Mensajes](
[IdMensaje] [int] IDENTITY(1,1) NOT NULL,
[IdCliente] [int] NULL,
[IdVendedor] [int] NULL,
[CorreoRemitente] [varchar](100) NULL,
[CorreoCliente] [varchar](100) NULL,
[CorreosAdicionales] [varchar](max) NULL,
[Tema] [varchar](100) NULL,
[Mensaje] [varchar](max) NULL,
[Fecha] [date] NULL,
[Hora] [time](5) NULL,
CONSTRAINT [PK_Mensajes] PRIMARY KEY CLUSTERED
[Archivos](
[IdArchivo] [int] IDENTITY(1,1) NOT NULL,
[IdMensaje] [int] NULL,
[Nombre] [varchar](max) NULL,
[Ubicacion] [varchar](50) NULL,
CONSTRAINT [PK_Archivos] PRIMARY KEY CLUSTERED
There are 2 ways to send messages in the system I'm developing...:
the first one is, you select a client, and when the message is sent, the client's ID (IdCliente) is taken from Clientes Table and inserted in a new row in Mensajes table.
that way, when you want to check all of the sent messages, you can clearly see to which client was sent.
the second one would be a free one, you send to whoever you want, you don't have to select a client. Therefore, when you register the new message, the IdCliente column would be null.
Some notes out of the way now: Vendedor is the seller or user who sent the message, Archivos means files, and in case you want to attach files, for each one of them, a new row in Archivos would be inserted and each with the same IdMensaje.
FOR THE moment, I have a query that allows me to see all my messages (with clients), and to also show how many files were attached to that message. HERE IT IS:
SELECT (Clientes.ApellidoPaterno + ' ' + Clientes.ApellidoMaterno + ' ' + Clientes.Nombre) AS Cliente, Mensajes.* FROM (SELECT Mensajes.IdMensaje, Mensajes.IdCliente, Mensajes.IdVendedor, Mensajes.CorreoRemitente, Mensajes.CorreoCliente, Mensajes.CorreosAdicionales, Mensajes.Tema, Mensajes.Mensaje, Mensajes.Fecha, Mensajes.Hora, COUNT(Archivos.IdArchivo) AS Archivos FROM Mensajes LEFT OUTER JOIN Archivos ON Mensajes.IdMensaje = Archivos.IdMensaje GROUP BY Mensajes.IdMensaje, Mensajes.IdCliente, Mensajes.IdVendedor, Mensajes.CorreoRemitente, Mensajes.CorreoCliente, Mensajes.CorreosAdicionales, Mensajes.Tema, Mensajes.Mensaje, Mensajes.Fecha, Mensajes.Hora) AS Mensajes JOIN Clientes ON Clientes.IdCliente = Mensajes.IdCliente ORDER BY Mensajes.Fecha DESC, Mensajes.Hora DESC
"(Clientes.ApellidoPaterno + ' ' + Clientes.ApellidoMaterno + ' ' + Clientes.Nombre) AS Cliente" Displays the full name of the client
however, now I want to have a query in which displays the same information as above... plus the following...
Messages with IdCliente = null
if IdCliente = null then the Cliente Column that I mentioned above would be "" (blank space)
If I need to be more specific, please let me know in what part must I give more information.
I hope you guys can help me
Thanks in advance
Firstly, change your JOIN to a LEFT JOIN:
LEFT JOIN Clientes ON Clientes.IdCliente = Mensajes.IdCliente
This will allow Messages with no associated IdCliente to remain. To get the desired blank space, use COALESCE:
COALESCE(Clientes.ApellidoPaterno + ' ' + Clientes.ApellidoMaterno + ' ' + Clientes.Nombre, '') AS Cliente

How to create a calculated column in a SQL Server 2008 table

I really need a calculated column on a table with simple sum.
Please see below:
SELECT key3
,SUM(UTOTALWBUD)
FROM CONTACT1
INNER JOIN CONTACT2
ON CONTACT1.ACCOUNTNO = CONTACT2.ACCOUNTNO
WHERE KEY1 = 'Client'
GROUP BY KEY3
I have tried to create a calculated column by adding following
ALTER TABLE ManagerTaLog
ADD WeeklyBudget as ( SELECT
key3
,SUM(UTOTALWBUD)
FROM CONTACT1
JOIN CONTACT2
ON CONTACT1.ACCOUNTNO = CONTACT2.ACCOUNTNO
WHERE KEY1 = 'Client'
GROUP BY KEY3)
I got the error message:
Msg 1046, Level 15, State 1, Line 4
Subqueries are not allowed in this context. Only scalar expressions are allowed.
Please advise what can I do about it.
Many thanks
Part 2
I have create a function; however, i get null values please advise.
CREATE FUNCTION [dbo].[SumIt](#Key3 varchar)
RETURNS TABLE
AS
RETURN
(
SELECT SUM(UTOTALWBUD)
FROM CONTACT1
JOIN CONTACT2
ON CONTACT1.ACCOUNTNO = CONTACT2.ACCOUNTNO
JOIN Phone_List
ON CONTACT1.KEY3 = Phone_List.[Manager ]
WHERE KEY1 = 'Client'
AND Phone_List.[Manager ] = #Key3
GROUP BY [Manager ]
)
END
GO
Just select statment that returns values I wish to add to Phone_list table
SELECT [Manager ]
,SUM(UTOTALWBUD)
FROM CONTACT1
JOIN CONTACT2
ON CONTACT1.ACCOUNTNO = CONTACT2.ACCOUNTNO
JOIN Phone_List
ON CONTACT1.KEY3 = Phone_List.[Manager ]
WHERE KEY1 = 'Client'
GROUP BY [Manager ]
Table definitions
CREATE TABLE [dbo].[CONTACT1](
[ACCOUNTNO] [varchar](20) NOT NULL,
[COMPANY] [varchar](40) NULL,
[CONTACT] [varchar](40) NULL,
[LASTNAME] [varchar](15) NULL,
[DEPARTMENT] [varchar](35) NULL,
[TITLE] [varchar](35) NULL,
[SECR] [varchar](20) NULL,
[PHONE1] [varchar](25) NOT NULL,
[PHONE2] [varchar](25) NULL,
[PHONE3] [varchar](25) NULL,
[FAX] [varchar](25) NULL,
[EXT1] [varchar](6) NULL,
[EXT2] [varchar](6) NULL,
[EXT3] [varchar](6) NULL,
[EXT4] [varchar](6) NULL,
[ADDRESS1] [varchar](40) NULL,
[ADDRESS2] [varchar](40) NULL,
[ADDRESS3] [varchar](40) NULL,
[CITY] [varchar](30) NULL,
[STATE] [varchar](20) NULL,
[ZIP] [varchar](10) NOT NULL,
[COUNTRY] [varchar](20) NULL,
[DEAR] [varchar](20) NULL,
[SOURCE] [varchar](20) NULL,
[KEY1] [varchar](20) NULL,
[KEY2] [varchar](20) NULL,
[KEY3] [varchar](20) NULL,
[KEY4] [varchar](20) NULL,
[KEY5] [varchar](20) NULL,
[STATUS] [varchar](3) NOT NULL,
[NOTES] [text] NULL,
[MERGECODES] [varchar](20) NULL,
[CREATEBY] [varchar](8) NULL,
[CREATEON] [datetime] NULL,
[CREATEAT] [varchar](5) NULL,
[OWNER] [varchar](8) NOT NULL,
[LASTUSER] [varchar](8) NULL,
[LASTDATE] [datetime] NULL,
[LASTTIME] [varchar](5) NULL,
[U_COMPANY] [varchar](40) NOT NULL,
[U_CONTACT] [varchar](40) NOT NULL,
[U_LASTNAME] [varchar](15) NOT NULL,
[U_CITY] [varchar](30) NOT NULL,
[U_STATE] [varchar](20) NOT NULL,
[U_COUNTRY] [varchar](20) NOT NULL,
[U_KEY1] [varchar](20) NOT NULL,
[U_KEY2] [varchar](20) NOT NULL,
[U_KEY3] [varchar](20) NOT NULL,
[U_KEY4] [varchar](20) NOT NULL,
[U_KEY5] [varchar](20) NOT NULL,
[recid] [varchar](15) NOT NULL
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
CREATE TABLE [dbo].[Phone_List](
[Manager ] [nvarchar](255) NULL,
[SalesCode] [nvarchar](255) NULL,
[Email] [nvarchar](255) NULL,
[PayrollCode] [nvarchar](255) NULL,
[Mobile] [nvarchar](255) NULL,
[FName] [nchar](20) NULL,
[idd] [tinyint] NULL,
[OD] [varchar](20) NULL,
[WeeklyBudget] AS ([dbo].[SumIt]([manager]))
) ON [PRIMARY]
You can wrap your query into the function like this (it HAS to return one value):
CREATE FUNCTION dbo.SumIt(#Key1 varchar(max))
returns float
as
begin
return (select sum(UTOTALWBUD) from
CONTACT1 inner join
CONTACT2 on
CONTACT1.ACCOUNTNO=CONTACT2.ACCOUNTNO
where KEY1=#key1
group by KEY3)
END
And use this function instead with calc field - something like this:
alter table ManagerTaLog add WeeklyBudget as dbo.SumIt(Key1)
NOTE
that it will be the performance killer for queries like that:
select * from ManagerTaLog
You should change your function in such a way, that is accept NOT varchar value, but NVARCHAR(255) - the same type as Manager column. Try it.
If you resolve the issue of returning two values, one solution would be to implement the calculation in a function and use this function for the calculated column.
Some considerations
I have assumed that it is Key3 that needs to be passed to the function and have added a where clause to return the weekly budget for the given Key3 value.
The function will get executed for every record in a resultset where you add the calculated column.
Script
CREATE FUNCTION dbo.fn_ManagerTaLogWeeklyBudget(#Key3 INTEGER) RETURNS INTEGER AS
BEGIN
select sum(UTOTALWBUD) from
CONTACT1 inner join
CONTACT2 on
CONTACT1.ACCOUNTNO=CONTACT2.ACCOUNTNO
where KEY1='Client'
AND KEY3 = #Key3
group by KEY3)
END
GO
ALTER TABLE dbo.ManagerTaLog
ADD WeeklyBudget AS dbo.fn_ManagerTaLogWeeklyBudget(Key3)
You cannot have a subquery that returns multiple values to be used as the computed column expression.
You can have an expression that returns a single value - or you can wrap your code in a stored function - or you can create a view (with a JOIN or subquery) that combines this logic into something you can use
IMHO It is a wrong way. You have to use trigger on CONTACT tables and update WeeklyBudget in these triggers.
You can have calculated columns in a table but that will be present (and calculated) in all rows of the table.
The thing what you're trying to do in your select is called "aggregate". Try this:
select key3, sum(UTOTALWBUD) as WeeklyBudget from
CONTACT1 inner join CONTACT2 on CONTACT1.ACCOUNTNO=CONTACT2.ACCOUNTNO
where KEY1='Client'
group by key3
CREATE FUNCTION [dbo].[SumIt](#Key3 varchar)
RETURNS TABLE
AS
RETURN
(
SELECT SUM(ISNULL(UTOTALWBUD,0))
FROM CONTACT1
JOIN CONTACT2
ON CONTACT1.ACCOUNTNO = CONTACT2.ACCOUNTNO
JOIN Phone_List
ON CONTACT1.KEY3 = Phone_List.[Manager ]
WHERE KEY1 = 'Client'
AND Phone_List.[Manager ] = #Key3
GROUP BY [Manager ]
)
END
GO
Hope this will help. Please note that this is not a proper solution, but this may help your particular scenario. Since the function is returning multiple values, you can either user a table valued function or just modify the query in a way that it will return only one value. But if the column is nullable please don't forget to add ISNULL.

Joining multiple columns in one table to a single column in another table

I am looking to create a view that pulls data from two tables "Schedule" and "Reference".
Schedule has 50+ columns (it's almost completely denormalized -- not my design), most of which contain a value that could be joined to a column in the Reference table.
How do I write the SQL statement to correctly join each column in Schedules to the single column in Reference?
The Schedule table is defined as:
CREATE TABLE [dbo].[Schedule](
[ID] [int] NOT NULL,
[SCHEDULEWEEK] [datetime] NOT NULL,
[EMPNO] [numeric](10, 0) NOT NULL,
[EMPLNAME] [varchar](32) NULL,
[EMPFNAME] [varchar](32) NULL,
[EMPSENDATE] [datetime] NULL,
[EMPHIREDATE] [datetime] NULL,
[EMPTYPE] [char](1) NULL,
[EMPSTATUS] [char](1) NULL,
[SNREFUSALS] [tinyint] NULL,
[QUALSTRING] [varchar](128) NULL,
[JOBOVERSHIFTTYPE] [bit] NULL,
[SHORTNOTICE] [bit] NULL,
[SHORTNOTICEWAP] [bit] NULL,
[SHORTNOTICEPHONE] [varchar](32) NULL,
[LEADHAND] [bit] NULL,
[DUALCURRENCY] [bit] NULL,
[MIN100WINDOW] [bit] NULL,
[STATHOLIDAY] [bit] NULL,
[AREAOVERHOURS] [bit] NULL,
[DOUBLEINTERZONES] [bit] NULL,
[MAXDAYSPERWEEK] [tinyint] NULL,
[MAXHOURSPERWEEK] [numeric](10, 2) NULL,
[MAXHOURSPERSHIFT] [numeric](10, 2) NULL,
[MAXDOUBLESPERWEEK] [tinyint] NULL,
[ASSIGNEDDAYS] [tinyint] NULL,
[ASSIGNEDHOURS] [numeric](10, 2) NULL,
[ASSIGNEDDOUBLES] [tinyint] NULL,
[ASSIGNEDLOAHOURS] [numeric](10, 2) NULL,
[SHIFTNO1] [int] NULL,
[TEXT1_1] [varchar](64) NULL,
[TEXT2_1] [varchar](64) NULL,
[DAYFLAG1] [bit] NULL,
[COMMENT1] [text] NULL,
[SHIFTNO2] [int] NULL,
[TEXT1_2] [varchar](64) NULL,
[TEXT2_2] [varchar](64) NULL,
[DAYFLAG2] [bit] NULL,
[COMMENT2] [text] NULL,
[SHIFTNO3] [int] NULL,
[TEXT1_3] [varchar](64) NULL,
[TEXT2_3] [varchar](64) NULL,
[DAYFLAG3] [bit] NULL,
[COMMENT3] [text] NULL,
[SHIFTNO4] [int] NULL,
[TEXT1_4] [varchar](64) NULL,
[TEXT2_4] [varchar](64) NULL,
[DAYFLAG4] [bit] NULL,
[COMMENT4] [text] NULL,
[SHIFTNO5] [int] NULL,
[TEXT1_5] [varchar](64) NULL,
[TEXT2_5] [varchar](64) NULL,
[DAYFLAG5] [bit] NULL,
[COMMENT5] [text] NULL,
[SHIFTNO6] [int] NULL,
[TEXT1_6] [varchar](64) NULL,
[TEXT2_6] [varchar](64) NULL,
[DAYFLAG6] [bit] NULL,
[COMMENT6] [text] NULL
-- Snip
) ON [PRIMARY]
And the Reference table is defined as:
CREATE TABLE [dbo].[Reference](
[ID] [int] NOT NULL,
[CODE] [varchar](21) NOT NULL,
[LOCATIONCODE] [varchar](4) NOT NULL,
[SCHAREACODE] [varchar](16) NOT NULL,
[LOCATIONNAME] [varchar](32) NOT NULL,
[FLTAREACODE] [varchar](16) NOT NULL
) ON [PRIMARY]
I am trying to join each [TEXT1_]/[TEXT2_] column in Schedule to the [SCHAREACODE] column in reference. All the reference table contains is a list of areas where the employee could work.
I think he means to join on the Reference table multiple times:
SELECT *
FROM Schedule AS S
INNER JOIN Reference AS R1
ON R1.ID = S.FirstID
INNER JOIN Reference AS R2
ON R2.ID = S.SecondID
INNER JOIN Reference AS R3
ON R3.ID = S.ThirdID
INNER JOIN Reference AS R4
ON R4.ID = S.ForthID
Your description is a bit lacking, so I'm going to assume that
Schedule has 50+ columns (it's almost completely denormalized -- not my design), most of which contain a value that could be joined to a column in the Reference table.
means that 1 of the 50+ columns in Schedule is a ReferenceId. So, given a table design like:
Schedule ( MaybeReferenceId1, MaybeReferenceId2, MaybeReferenceId3, ... )
Reference ( ReferenceId )
Something like:
SELECT *
FROM Schedule
JOIN Reference ON
Schedule.MaybeReferenceId1 = Reference.ReferenceId
OR Schedule.MaybeReferenceId2 = Reference.ReferenceId
OR Schedule.MaybeReferenceId3 = Reference.ReferenceId
OR Schedule.MaybeReferenceId4 = Reference.ReferenceId
...
would work. You could simplify it by using IN if your RDBMS supports it:
SELECT *
FROM Schedule
JOIN Reference ON
Reference.ReferenceId IN (
Schedule.MaybeReferenceId1,
Schedule.MaybeReferenceId2,
Schedule.MaybeReferenceId3,
Schedule.MaybeReferenceId4,
...
)
From updated question
Perhaps something like this? It will be messy no matter what you do.
SELECT S.ID
S.TEXT1_1,
TEXT1_1_RID = COALESCE((SELECT MAX(R.ID) FROM Reference R WHERE R.SCHAREACODE = S.TEXT1_1), 0),
S.TEXT1_2,
TEXT1_2_RID = COALESCE((SELECT MAX(R.ID) FROM Reference R WHERE R.SCHAREACODE = S.TEXT1_2), 0),
...
FROM Schedule S
Agree with TheSoftwareJedi, but can I just suggest using LEFT JOINs so that failures-to-match don't cause your Schedule row to disappear?
Of course, doing 28 JOINs is going to be a bit cumbersome whatever the details.
I'm not sure I'd call this "denormalized", more "abnormalized" ... :-)
Try a query like this:
select s.*, r.schareacode from schedule s,
where
s.text1_1 = s.schareacode
or s.text2_1 = s.schareacode
or s.textx_x = s.schareacode
..
You should be able to get the same results with traditional joins so I recommend you experiment with that as well.