SQL: Showing a Link Option Using Conditions - sql

I'm trying to make a menu option available to users depending on conditions using 2 tables.
We have a directory website for different towns and each town administrator is able to add subcategories to main categories in order to customise their directory structure, but it's currently allowing them to add subcategories to further subcategories that are already populated with adverts - which it shouldn't do.
I would like to only show the option to 'Add a subcatgory' to another subcategory when there are no adverts already added to it.
For example, there is a subcategory called 'Accountants' in all town directories. It is populated with client adverts in the 'Brentwood' directory, but not in the 'Shrewsbury' directory.
I want to only show the option to 'Add a subcategory', if the following three conditions are met using data from the following tables and columns:
'Directories' Table: It needs to be the current franchise town (using the 'FranchiseGID' column)
'Directories' Table: It must apply to each individual subcategory (GID column)
'Clients' Table: No Client have registered to be listed in the subcategory (GID column)
So using a combination of these table columns, using the 'iAdvertCount' as the counter and 'ClientGID' to check whether there are advert subscriptions in that particular subcategory; also that it's only being applied to the current franchise website (FranchiseGID).
I can select information from one table, but not multiple - so would love some help on how to select the reqiured info from the above columns and tables.
Here is how far I've got with the 'Directories' table, but need to add in the Client GID condition from 'Clients' too:
Dim iAdvertCount
SQLCommand = "SELECT COUNT(*) AS Counter FROM Directories WHERE GID is not null AND FranchiseGID is not null"
rsTemp.Open SQLCommand, objConn, adOpenStatic, adLockReadOnly
iSubscriptionCount = rsTemp("Counter")
rsTemp.Close
if iSubscriptionCount = 0 then
%><% = GetIcon("Add", "Add Sub Directory", 25, True) %><%
end if
So far I am able to hide the 'Add a subcategory' option on any website franchise (e.g. Brentwood) directory that contains a ClientGID, but it's being applied to all subcategories regardless of whether they contain Client Subscriptions/Adverts or not.
I am hoping someone can help and indeed make sense of what I wrote to assist in some way as it would help a lot!

I'm hoping this is more than a proverbial shot in the dark
I believe you need to use an exist operator.
The exist operator is used in the where clause to determine if subquery returns results or not.
So your query might look like:
SELECT COUNT(*) AS Counter
FROM Directories
WHERE GID is not null
AND FranchiseGID is not null
AND NOT EXISTS(SELECT 'ARBITRARY VALUE'
FROM clients
WHERE clients.gid = directories.gid)
The exists operator takes advantage of a subquery that's why we're able to refer to the directories table even though it's not in the subquery's from clause. So if the subquery returns anything it will not be returned to be counted by the count function
You'll probably need to change the subquery's where clause in order to get it to work correctly since I'm not sure how your database is structured

Related

MS Access - Query - Required Forms for Each Employee

I have 3 tables, all SharePoint lists. I am trying to create a query that will show me all of the required DQ_File Forms that do not have an attachment in the DQ_File.
DQ_File_Lookup is a lookup table for the description field in the DQ_File. It also has the "DQRequired" flag I am looking for to see all of the required fields that do not have an attachment.
I have included a screen shot showing the table layouts and relations.
Any help would be appreciated, I am sure I am just overlooking something obvious.
A example would be as follows:
Employee Name | Document Name
You would have employee Joe and he has forms A,B,D out of a possible forms A,B,C,D,E,F so he would be missing forms C,E and F.
So the employee name would come from the employee table, and the document name needs to get passed through the DQ_File Table from the DQ_File_Lookup
the way I thought to do it was to get it to show all documents from the DQ_File table that are missing, that I can do. But that only shows the information that has an entry. There are certain forms that are required for every employee that I want to be able to see if a employee is missing any of those forms.
Using what #June7 posted below I got it to work, and it now will show me all 15 documents that are required for every driver. But when I add the attachment field from DQ_File it shows them all as zero attachments, when I know some of them do indeed have attachments already.
Here is a screen cap showing this.
Williams in particular should only have about 5 documents that should be on this list, but instead it is showing like all 15 are missing.
Here is the SQL from the combined query:
SELECT [qryEmployees+DQFileLookup].Last, [qryEmployees+DQFileLookup].Description, DQ_File.Attachment
FROM DQ_File RIGHT JOIN [qryEmployees+DQFileLookup] ON DQ_File.EmployeeNo = [qryEmployees+DQFileLookup].EmployeeCode
WHERE (((DQ_File.Attachment.FileURL) Is Null) AND (([qryEmployees+DQFileLookup].CURRENT)=True) AND (([qryEmployees+DQFileLookup].DRIVER)=True) AND (([qryEmployees+DQFileLookup].DQRequired)=True));
If you want to know which required docs employees do not have, then need a dataset of all possible combinations of employees/docs. Then match that dataset with DQ_File to see what is missing. The all combinations dataset can be generated with a Cartesian query (a query without JOIN clause) - every record of each table will associate with every record of other table.
SELECT Employees.*, DQ_File_Lookup.* FROM Employees, DQ_File_Lookup;
Then join that query with DQ_File.
SELECT Query1.EmployeeID, Query1.First, Query1.Last, Query1.ID, Query1.Title, Query1.DQRequired, DQ_File.Description, DQ_File.EmployeeNo
FROM DQ_File RIGHT JOIN Query1 ON (DQ_File.EmployeeNo = Query1.EmployeeID) AND (DQ_File.Description = Query1.ID)
WHERE (((Query1.DQRequired)=True) AND ((DQ_File.EmployeeNo) Is Null));
Advise not to use exact same field names in multiple tables. For instance, Title in DQ_File_Lookup could be DocTitle and Title in Employees could be JobTitle. And there will be less confusion if ID is not used as name in all tables.
It seems unnecessary to repeat Title and [Compliance Asset ID] in all 3 tables.
Strongly advise not to use spaces in naming convention. Title case is better than all upper case.

SQL Server Full Text Search: One to many relationships

I am trying to retrieve data from tickets that meet search matches. The relevant bits of data here are that a ticket has a name, and any number of comments.
Currently I'm matching a search against the ticket name like so:
JOIN freetexttable(Tickets,TIC_Name,'Test ') s1
ON TIC_PK = s1.[key]
Where the [key] from the full text catalog is equal to TIC_PK.
This works well for me, and gives me access to s1.rank, which is important for me to sort by.
Now my problem is that this method wont work for ticket searching, because the key in the comment catalog is the comment PK, an doesn't give me any information I can use to link to the ticket.
I'm very perplexed about how to go about searching multiple descriptions and still getting a meaningful rank.
I'm pretty knew to full-text search and might be missing something obvious.
Heres my current attempt at getting what I need:
WHERE TIC_PK IN(
SELECT DES_TIC_FK FROM freetexttable(TicketDescriptions, DES_Description,'Test Query') as t
join TicketDescriptions a on t.[key] = a.DES_PK
GROUP BY DES_TIC_FK
)
This gets me tickets with comments that match the search, but I dont think it's possible to sort by the rank data freetexttable returns with this method.
To search the name and comments at the same time and get the most meaningful rank you should put all of this info into the same table -- a new table -- populated from your existing tables via an ETL process.
The new table could look something like this:
CREATE TABLE TicketsAndDescriptionsETL (
TIC_PK int,
TIC_Name varchar(100),
All_DES_Descriptions varchar(max),
PRIMARY KEY (TIC_PK)
)
GO
CREATE FULLTEXT INDEX ON TicketsAndDescriptionsETL (
TIC_Name LANGUAGE 'English',
All_DES_Descriptions LANGUAGE 'English'
)
Schedule this table to be populated either via a SQL job, triggers on the Tickets and TicketDescriptions tables, or some hook in your data layer. For tickets that have multiple TicketDescriptions records, combine the text of all of those comments into the All_DES_Descriptions column.
Then run your full text searches against this new table.
While this approach does add another cog to the machine, there's really no other way to perform full text searches across multiple tables and generate one rank.

Do I really need a relation table in my case?

Lets say I have a module. I build an interface where user can assign the module to groups.
Lets say currently I have 3 groups. In the UI the user would choose all 3 groups to assign the module to them. For examle in a multiple selectbox.
The intention of the user is to assign the module to ALL groups.
I guess I would need a many to many relation table. My source code would execute a sql query to insert 3 entries.
But wait. What if two weeks later the admin adds a new group... In the relation table are only 3 entries. And the user wonders why the module is not assigned to the new added group.
What would be an elegant solution? I need definite to update the relation table, or I make a new column -called, lets say "groups"- in my module table where I add the assigned groupsIds in this format: "1;2;7;15" or the keyword "All".
The advantage would be that with the keyword "All" I could know in my code that the module is assigned to all groups.
With the relation table I do not have this option. In addition I do not need to assign a group to a module. I just need to assign a module to groups.
In my opinion I do not need a relation table in this case.
What would you say? Or do you have another approach?
To make a many-to-many relation, you need a relation table.
To use a solution like putting comma separated values in a field to make multiple relations only works if you fetch data from separate tables in separate queries, when you need to use that to join the data in the database, it becomes very complicated very fast.
In a relation table you could use a null value to mean "all". In this example the modules 1 and 2 are members of the group 1 only, and the module 3 is a member of all groups:
ModuleId GroupId
-------- -------
1 1
2 1
3 null
To fetch data for groups using a relation like that you would use a query like:
select
g.GroupName,
m.ModuleName
from
Groups g
inner join GroupModules gm on gm.GroupId is null or gm.GroupId = g.GroupdId
inner join Modules m on m.ModuleId = gm.ModuleId
Another alternative is to use the relation table as usual, and add a property on the module that it's a member of all groups. When you add a new group, you would also add records in the relation table for all modules that are members of all groups. Example (in T-SQL):
create procedure Group_Add
#GroupName varchar(50)
as
set nocount on
declare #id int
insert into Groups (
GroupName
) values (
#GroupName
)
set #id = scope_identity()
insert in GroupModules (
GroupId,
ModuleId
)
select
#id, ModuleId
from
Modules
where
IsInAllGroups = 1
The typical database pattern for this is 3 tables, Module, Group, GroupModule. That is how many to many relationships are properly handled in database design.
How to get the data populated is a problem for your UI. You can indeed have a pull down list that includes the word all for them to use in choosing the groups a module will be associated with. It can even be the default. What you do is write code to interpret ALL to insert one record for every group.
NOw if you are adding groups as well as modules, you also need a process to make sure that when a group is added all those which shoudl have all groups get added to the new group. Personally I would put an IS_ALL flag on the Module table to make this easier. Then you know which moduels have been selected for all groups. You will need to make sure that if someone goes back and changes the module to specificgroups instead of all that this field is updated.

Need query to return Null fields if no associated data in joined table

I'm trying to design a query in Access 2003 that returns information for a directory. One of the values I wish to include is contained in a second table, but that table doesn't have records for all the records I want.
I want a directory line for EACH MomID in table Registration, regardless of whether that MomID is referenced in table Kids. I can't just leave it out, because if there are children listed in Kids, that needs to be in the directory.
Currently, my query returns only those records where MomID is in both Registration and Kids. I'm filtering the data so that I only get records from Registration with the current YearStart field, so I can't make it an OR statement (even if I knew how). How do I get EVERY record in Registration which meets the YearStart criterion, and have the query return an empty field if the relevant MomID isn't present in Kids"
I could just go manually add MomID to Kids and leave the other fields blank, but that solution lacks elegance. I'm also not entirely sure that would work, since I'm running the data in Kids through two other queries to sort and concatenate before pulling it into the directory query. Plus, I'd like to retain the ability to include childless individuals in the directory if I ever want to expand the database to include the volunteers associated with the group.
What you're describing is a classic case of LEFT JOIN. This operator brings ALL records from the left part of the join even if there're no matches in the right part,
In your case it would be something like
SELECT * FROM Registration LEFT JOIN Kids ON Registration.MomID = Kids.MomID
WHERE Registration.YearStart = Year(Date())
Substitute * with only needed fields if you don't need all the fields from both tables.

Crystal Reports SQL Expression

I am having trouble figuring out how to pull a value from a secondary table, to use as selection criteria on a per-record basis.
I am working with Crystal Reports 2011 on Windows 7, over an ODBC connection to an Oracle 11g database.
I am creating an employee directory that utilizes information from two locations:
table: TEACHERS
view: PVSIS_CUSTOM_TEACHERS
The teachers table is set up with your predictable fields: id, lastname, firstname, telephone, address, city, state, zip, etc. etc. etc.
The view has the following fields available:
TEACHERID
FIELD_NAME
TEXT_VALUE
The database application I am using allows me to create "custom fields" that are related back to the main teachers table. In truth, the fields I am creating are actually stored in a separate table, but are then accessible through the PVSIS_CUSTOM_TEACHERS view. Since the database application allows for any number of "custom fields" to be created, the view can have any number of records in it that can be tied back to the records within the teachers table.
There are MANY custom fields that have been created, but for the purposes of my current project, only 3 of them matter:
empDirSupRecord
empDirSupPhone
empDirSupAddr
The view for my personal teacher record would look like this:
TeacherID Field_Name Text_Value
1 empDirSupRecord
1 empDirSupPhone 1
1 empDirSupAddr 1
1 AnotherField another_value
1 YetAnotherField yetanother_value
(This would indicate that I've asked for my phone and address to be suppressed, but would still want my name to be included in the directory)
These fields will each contain a '1' if the user has asked that their phone number, or address not be published, or if we need to suppress the entire record altogether.
When I first started my report, I pulled both the table and view into the database expert and linked them together with teachers.id = pvsis_custom_teachers.teacherid. However, this causes each teacher's name to print on the report once for every record with their teacher id in the view. Since that's not the behavior I want, I removed the view from the database expert, and tried using SQL Expression fields to retrieve the contents of the custom field. This is where I'm currently stuck. I would need to write the sql in a way that selects the correctly named field, for each of the teacher records as the record is being processed by the report.
Currently, my sql expressions statement is written as:
(SELECT text_value FROM pvsis_custom_teachers WHERE field_name = 'empDirSupRecord' AND teacherid = '1')
What I need to do is figure out how to get the report to intelligently select the record for teacherid = (whatever teacherid is currently being processed). I'm not sure if SQL Expression fields are the way to go to accomplish this, so am definitely open to alternate suggestions if my current approach will not work.
Thanks for taking a look. :-)
You're almost there with the SQL Expression. You can refer back to the main query with double quoted field names. So what you're looking for is:
case when "teacher"."id" is null then null
else (SELECT max(text_value)
FROM pvsis_custom_teachers
WHERE field_name = 'empDirSupRecord' AND teacherid = "teacher"."id")
end
Note that CR will likely complain without the null check and use of max(), since it wants to be sure that only a scalar will ever be returned.
The alternative, and likely less-performance-intensive way to do this, is to join the table and view like you first described. Then, you can group by {teacher.id} and keep track of each field name in the view via variables. This will require more work and more formulas, though. Something like this, for example:
//Place this formula in the Group Header
whileprintingrecords;
stringvar empDirSupRecord:="";
//Place this formula in the Details section
whileprintingrecords;
stringvar empDirSupRecord;
if {pvsis_custom_teachers.field_name} = 'empDirSupRecord'
then empDirSupRecord:={pvsis_custom_teachers.text_value}
//Place this formula in the Group Footer
whileprintingrecords;
stringvar empDirSupRecord;