Setting up database structure - sql

I am trying to figure out the best way to restructure my database as I didn't plan ahead and now I am a little stuck on this part :)
I have a Table called Campaigns and a Table called Data Types.
Each campaign is a unique record that holds about 10 fields of data.
The data types contains 3 fields - ID, Type, Description
When You create a campaign, you can select as many data types as you would like.
1, 2 or all 3 of them.
My concern / question is - How can I store what the user selected with the campaign record?
I need to be able to pull in the campaign details but also know which data types were selected.
How I originally had it set up was the data types were in 1 field, comma separated but learned is not ideal to do that.
What would be the best way to accomplish this? Storing the data as XML ?
UPDATE -
Here is an example of the query I was trying to get to work (its probably way off).
BEGIN
SET NOCOUNT ON;
BEGIN
SELECT *
FROM (SELECT A.[campaignID] as campaignID,
A.[campaignTitle],
A.[campaignDesc],
A.[campaignType],
A.[campaignStatus],
A.[duration],
A.[whoCreated],
B.[campaignID],
B.[dataType],
(SELECT *
FROM Tags_Campaign_Settings
WHERE campaignID = #campaignID) AS dataTypes
FROM Tags_Campaigns AS A
INNER JOIN
Tags_Campaign_Settings AS B
ON A.[campaignID] = B.[campaignID]
WHERE A.[campaignID] = #campaignID
) AS a
FOR XML PATH ('campaigns'), TYPE, ELEMENTS, ROOT ('root');
END
END

Create a join table called Campain_DataType with campaignId and dataTypeId. Make sure they're foreign key constrained to the respective tables. When you query for campaign data, you can either create a separate query to get the data type information based on the campaignId, or you can do a left outer join to fetch campaigns and their data types together.
If you want to collapse the 3 data types into the same row, then give the following a shot. It's definitely on the hacky side, and it'll only work with a fixed number of data types. If you add another data type, you'll have to update this query to support it.
SELECT
Campaign.ID,
Campaign.foo,
Campaign.bar,
dataType1.hasDataType1,
dataType2.hasDataType2,
dataType3.hasDataType3
FROM
Campaign
LEFT OUTER JOIN
( SELECT
1 as hasDataType1,
Campaign_DataType.campaignID
FROM
DataType
INNER JOIN Campaign_DataType ON Campaign_DataType.dataTypeId = DataType.id
WHERE
DataType.Type = 'Type1'
) dataType1 ON dataType1.campaignID = Campaign.ID
LEFT OUTER JOIN
( SELECT
1 as hasDataType2,
Campaign_DataType.campaignID
FROM
DataType
INNER JOIN Campaign_DataType ON Campaign_DataType.dataTypeId = DataType.id
WHERE
DataType.Type = 'Type2'
) dataType2 ON dataType2.campaignID = Campaign.ID
LEFT OUTER JOIN
( SELECT
1 as hasDataType3,
Campaign_DataType.campaignID
FROM
DataType
INNER JOIN Campaign_DataType ON Campaign_DataType.dataTypeId = DataType.id
WHERE
DataType.Type = 'Type3'
) dataType3 ON dataType3.campaignID = Campaign.ID
The record you receive for each Campaign will have three fields: hasDataType1, hasDataType2, hasDataType3. These columns will be 1 for yes, NULL for no.

Looks to me like what you want here is a crosstab query. Take a look at:
Sql Server 2008 Cross Tab Query

Related

How to Select table for Join using Case in SQL

i have a table attribute_name in which a column c_type indicate what type of value we have like 1,2,3,4 so that base on that value i decide which table to join .
so i select that table first Join (case statment) On (case statment)
but i does not work.
SELECT attribute_names.*,attributes_trans_name.*,
(CASE
WHEN attribute_names.c_type=1
THEN attribute_values_text.c_fk_files_id
WHEN attribute_names.c_type=3
THEN attribute_values_longtext.c_fk_files_id
WHEN attribute_names.c_type=8
THEN attribute_values_file.c_fk_files_id
END) as file_id
From attributes_trans_name,
attribute_names JOIN
(CASE
WHEN attribute_names.c_type=1
THEN attribute_values_text
WHEN attribute_names.c_type=3
THEN attribute_values_longtext
WHEN attribute_names.c_type=8
THEN attribute_values_file
END)
ON
(CASE
WHEN attribute_names.c_type=1
THEN attribute_values_text.c_fk_attribute_names_id
WHEN attribute_names.c_type=3
THEN attribute_values_longtext.c_fk_attribute_names_id
WHEN attribute_names.c_type=8
THEN attribute_values_file.c_fk_attribute_names_id
END) = attribute_names.c_id
WHERE
attribute_names.c_id=attributes_trans_name.c_fk_attribute_names_id
With proper JOIN/LEFT JOIN context, you can do in single query. Left join means I want the record from the left side always, but OPTIONAL if there is a match on the right side. So, I have adjusted your query to reflect. I have also rewritten to use "alias" names for the file names so it is shorter for read and write than bulky long table names.
So, the main table is the attribute_names as that appears to be the basis of all the joins with the C_ID column into each of the others. Notice indentation helps me know / follow what is linked to what, and not just all tables listed in bulk.
Now, by having each of the left-joins in place, it will ALWAYS TRY to link to their respective other tables by the foreign key, but as you know your data, only one of them will really have the piece of information you need. So your CASE construct is simplified down. If = 1, then look at the ATV (alias) table and its column, otherwise AVLT alias if = 3 and finally AVF if = 8
SELECT
AN.*,
ATN.*,
CASE WHEN AN.c_type = 1
THEN ATV.c_fk_files_id
WHEN AN.c_type = 3
THEN AVLT.c_fk_files_id
WHEN AN.c_type = 8
THEN AVF.c_fk_files_id END as file_id
From
attribute_names AN
JOIN attributes_trans_name ATN
ON AN.c_id = ATN.c_fk_attribute_names_id
LEFT JOIN attribute_values_text AVT
ON AN.c_id = AVT.c_fk_attribute_names_id
LEFT JOIN attribute_values_longtext AVLT
ON AN.c_id = AVLT.c_fk_attribute_names_id
LEFT JOIN attribute_values_file AVF
ON AN.c_id = AVF.c_fk_attribute_names_id

Using Select * in a SQL JOIN returns the wrong id value for the wrong table

I have two tables (PlayerDTO and ClubDTO) and am using a JOIN to fetch data as follows:
SELECT * FROM PlayerDTO AS pl
INNER JOIN ClubDTO AS cl
ON pl.currentClub = cl.id
WHERE cl.nation = 7
This returns the correct rows from PlayerDTO, but in every row the id column has been changed to the value of the currentClub column (eg instead of pl.id 3,456 | pl.currentClub 97, it has become pl.id 97 | pl.currentClub 97).
So I tried the query listing all the columns by name instead of Select *:
SELECT pl.id, pl.nationality, pl.currentClub, pl.status, pl.lastName FROM PlayerDTO AS pl
INNER JOIN ClubDTO AS cl
ON pl.currentClub = cl.id
WHERE cl.nation = 7
This works correctly and doesn’t change any values.
PlayerDTO has over 100 columns (I didn’t list them all above for brevity, but I included them all in the query) but obviously I don’t want to write every column name in every query.
So could somebody please explain why Select * changes the id value and what I need to do to make it work correctly? All my tables have a column called id, is that something to do with it?
SELECT *... is, according to the docs...
shorthand for “select all columns.” (Source: Dev.MySQL.com
Both your tables have id columns, so which should be returned? It's not indicated, so MySQL makes a guess. So select what you want to select...
SELECT pl.id, *otherfieldsyouwant* FROM PlayerDTO AS pl...
Or...
SELECT pl.* FROM PlayerDTO AS pl...
Typically, SELECT * is bad form. The odds you are using every field is astronomically low. And the more data you pull, the slower it is.

Create table with values from CTE based on rowid

i am fasing a situation when i want select some data to created table. This data must be in joined between 2 tables based on a rowid. I know this can be done like i wrote, but it's not a correct way. Could you please suggest me a good way of my code.
CREATE TABLE new_bi_ccm_agg_mth AS
WITH
RID_SEC AS
(
SELECT rowid
FROM bi_ccm_agg_mth t left join Base_ccm m on t.sub_id = m.sub_id and t.current_brand =
m.current_brand
)
select agg.* ,base.CURRENT_SEC FROM bi_ccm_agg_mth agg join base_ccm base
where rid_sec.rowid = agg.r owid

SQL Query Pull data from another table IF NULL

I have a system running a SQL Server Express database and I need to pull some data from it. I have the basic SQL query created but I have found that some data is located elsewhere.
The basic premise is I have a database of Repair Orders, Vehicles And Customers. The Vehicles are usually added via a VIN decoder so they have ID's associated from a MAKE and MODEL table. However in the case of a VIN not decoding the application allows the user to manually enter this information and then it is stored in another table named "UserVehicleAttributes". In this table there is the VehicleID, AttributeName, & AttributeValue.
UserAttributeId VehicleId AttributeName AttributeValue
-----------------------------------------------------------
364 6829 Model Sedona
365 6830 Make Kia
366 6830 Model Sedona
So what I need is if the Make or Model comes up as NULL from the Vehicle table, I can display what as manually entered in.
I found that there is an existing function in the DB that looks to be able to do what I need but I don't know how to use it as part of my query.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [SM].[fnVehicleModelName]()
RETURNS TABLE
AS
RETURN
(
SELECT DISTINCT v.VehicleId,
CASE
WHEN v.SubModelId IS NULL THEN ISNULL(ua.[AttributeValue],'')
ELSE smm.[Name]
END as Model
FROM SM.Vehicle v (NOLOCK)
LEFT OUTER JOIN
(SELECT sm.SubModelId, m.[Name] + ' ' + sm.[Name] as Name
FROM DMV.SubModel (NOLOCK) sm
INNER JOIN DMV.Model m (NOLOCK)
ON sm.ModelId = m.ModelId ) as smm
ON v.SubModelId = smm.SubModelId
LEFT OUTER JOIN SM.UserVehicleAttributes ua (NOLOCK)--
ON v.VehicleId = ua.VehicleId and ua.AttributeName = 'Model'
Any help is greatly appreciated. I am not very good with SQL (obviously) but I am trying to figure this one out.
I'm not sure why you're making this a function with no parameters - that's kinda the same thing as a view. Consider if using a view here might simplify the situation.
You're correct that ISNULL is what you want to use here, but I think the join should be more simple. Your situation is basically "pull the column value from whichever table has a non-null value, giving preference to one table first"
In the outer join, all the columns from the outer joined tables will be null if there's not a match, and if there is a match, all the values should be filled in. Knowing that... you should be able to do something like this... (as an example to clarify how this concept works, not solving your query for you)
select v.VehicleId,
VehicleName = isnull(Model.Name, UserVehicle.Name)
from Vehicle v
left outer join Model on Model.VehicleID = Vehicle.VehicleID
left outer join UserVehicle on UserVehicle.VehicleID = Vehicle.VehicleId
So, what that does is join the possible rows from either table, and the ISNULL macro selects whichever value is non-null. Do that for the rest of the columns, and fix the join condition to whatever your conditions are, and you should be golden.
That function has no parameters, if you want to use it rewrite it as a view, but it only shows model, so you can use subqueries like this:
SELECT
VehicleId,
CASE
WHEN Make IS NULL
THEN ( SELECT AttributeValue FROM UserVehicleAttributes
WHERE VehicleId = Vehicles.VehicleId
AND AttributeName = 'Make' )
ELSE Make
END AS Make,
CASE
WHEN Model IS NULL
THEN ( SELECT AttributeValue FROM UserVehicleAttributes
WHERE VehicleId = Vehicles.VehicleId
AND AttributeName = 'Model' )
ELSE Model
END AS Model
FROM
Vehicles

SQL Query to retrieve data while excluding a set of rows

I have basically four tables (SQL Server):
Objects:
id
ObjectName
Components
id
ComponentName
ObjectsDetails:
ObjectID
ComponentID
ExclusionTable
id
ComponentID
Basically, these tables describe Objects and what Objects are made of (what components)
For example, Object "A" may be made out of component "A" and component "B".
In this case, the tables would be populated this way:
Objects:
id ObjectName
1 A
Components:
id ComponentName
1 A
2 B
ObjectDetails:
ObjectID ComponentID
1 1
1 2
Now, "ExclusionTable" may have a list of components that are to be excluded from a search (therefore, excluding entire objects if the object is made out of at least one of those components).
For example, I would like to ask:
"Give me all the Objects that are not made out of components A and B".
Therefore, my question is:
Is there a way to write a query for that ? No views, no stored procedures please.. my SQL engine does not support that.
I tried something like:
SELECT DISTINCT ObjectName FROM Objects INNER JOIN ObjectsDetails ON Objects.id =
ObjectDetails.ObjectID WHERE ObjectsDetails.ComponentID NOT IN (1,2)
in case ExclusionTable tells us that Components A and B needs to be excluded.
Of course, that doesn't work...
I tried a few variations using WHERE NOT EXISTS (SELECT * FROM ExclusionTable) but I am not proficient enough in SQL to understand how to get it to work using one query only (if it is even possible).
Thanks!
You should avoid doing queries with [not] in (select ...)
SELECT DISTINCT ObjectName
FROM Objects
INNER JOIN ObjectsDetails ON Objects.id = ObjectDetails.ObjectID
LEFT JOIN ExclusionTable on ExclusionTable.ComponentId = ObjectsDetails.ComponentID
where ExclusionTable.ComponentId is null;
This will retrieve only rows for which the ComponentID is not in ExclusionTable.
Update:
SELECT ObjectName
FROM Objects
INNER JOIN ObjectsDetails ON Objects.id = ObjectDetails.ObjectID
LEFT JOIN ExclusionTable on ExclusionTable.ComponentId = ObjectsDetails.ComponentID
group by ObjectName
having count(distinct ObjectsDetails.ComponentID) = sum(case when ExclusionTable.id is null then 1 else 0 end)
New approach, I think the only other way I could do it is basically to compare the number of components per object with the number of components in the object not included on the list. When these number are equal, no component is on the excluded list and we can show the object.
I'm sorry I can't make a test right now, please use EXPLAIN select ... to compare the queries, if they work.
Basically, if you need to get all objects not made from A or B, you need to get all objects EXCEPT those made from A or B.
SELECT DISTINCT Id, ObjectName
FROM Objects
WHERE Id NOT IN (
SELECT DISTINCT ObjectDetails.ObjectID
FROM ObjectDetails
INNER JOIN Components ON ObjectDetails.ComponentID = Components.Id
WHERE Components.ComponentName = 'A' OR Components.ComponentName = 'B'
)
Would that be what you're looking for?
EDIT: Of course, you can omit the join if you already have the component ids - then just put those in the where clause to filter them out.
select id, objectname
from Objects
left outer join
( select objectid from ObjectsDetails od inner join Exclusiontable et
on od.ComponentID= et.ComponentID) excludedid
on Objects.ID = excludedid.ObjectID and excludedid.ObjectID is null