I've been thrown quite the scenario today. Essentially, I have one table (ProjTransPosting) that houses records, and that table relates to a number of similarly structured tables (ProjCostTrans, ProjRevenueTrans, etc). They relate by TransId, but each TransId will relate to only one of the number of child tables (meaning if a TransId of 137 exists in ProjCostTrans, there cannot be a TransId of 137 in ProjRevenueTrans). The schemas of the children tables are identical.
So, my original thought was to create a Map and create the mappings from the various children tables. And then I would use this Map as a datasource in the form so everything can show up in one column. I created all the relationships between the Map and the children table along with the relation to the parent table. I put Map in the form as a datasource and this caused a blank Grid, although I don't know why. Is it the case that the Map object can only by of one table type at any given time? I thought the purpose of this was that it could be universal and act as a buffer to many record types. I'd like to pursue this route as this definitely would achieve what I'm looking for.
In failing this I was forced to arrange my Data Source to perform something like this: SELECT ProjTransPosting LEFT JOIN ProjCostTrans LEFT JOIN ProjRevenueTrans ... The problem with this is, each child table I add-on, it's creating additional columns, and the values of the other columns are all NULL (blank in AX). So I have something like this:
Parent.TransId ChildA.Field ChildB.Field ChildC.Field
1 NULL 1256 NULL
2 1395 NULL NULL
3 NULL 4762 NULL
4 NULL NULL 1256
Normally, the user would deal with the annoyance of having the extra columns show up, but they want to also be able to filter on the fields in all the children tables. My example above, they want to be able to filter "1256" and the results would return TransIds 1 and 4, but obviously since the values in this case are spread out in multiple columns, this cannot be done by the user.
Ideally the Map would "combine" these columns into one and then the user could filter easily on it. Any ideas on how to proceed with this?
Try creating a union query and then a view based on that query.
Maps are supposed to be used only in X++, and not as data sources in forms.
This sounds like the exact purpose of table inheritance in AX 2012.
http://msdn.microsoft.com/en-us/library/gg881053.aspx
When to use:
http://msdn.microsoft.com/en-us/library/gg843731.aspx
EDIT: Adding my comments here to make this a more full answer.
Let's say you have three tables TabPet, TabPetCat, TabPetDog, where TabPet is the supertype table and the others are decedents.
If you insert two records each into TabPetCat and TabPetDog (4 total), they will all have unique recIds. Let's say TabPetCat gets 5637144580 and 5637144581. TabPetDog gets 5637144582, and 5637144583.
If you open TabPet, you will see 5637144580, 5637144581, 5637144582, and 5637144583.
So what you would do is make your table ProjTransPosting the supertype and then ProjCostTrans, ProjRevenueTrans, etc descendant tables. Unless transId is really necessary, you could just get rid of it and only use RecId.
Related
I have a table that is actually a ranking list. I want to give user a chance to rearrange that top the way he wants, ergo, allow him to move the rows in that table. Should I create a separate column that would hold the place, or can it be done using embedded order in table?
The documentation says:
If a SELECT statement that returns more than one row does not have an ORDER BY clause, the order in which the rows are returned is undefined.
(This is true for all SQL databases.)
So you cannot rely on the order that the rows happen to be stored in; you have to use some value in some table column.
I am working on a CR where I need to create a PL/SQL package and I am bit confused about the approach.
Background : There is a View named ‘D’ which is at end of the chain of interdependent views in sequence.
We can put it as :
A – Fact table (Populated using Informatica, source MS-Dynamics)
B – View 1 based on fact table
C – View 2 based on View1
D – View 3 based on view2
Each view has multiple joins with other tables in structure along with the base view.
Requirement: Client wants to remove all these views and create a PL/SQL Package which can insert data directly from MS-Dynamics to View3 i.e., ‘D’.
Before I come up with something complex. I would like to know, is there any standard approach to address such requirements.
Any advice/suggestions are appreciated.
It should be obvious that you still need a fact table to keep some data.
You could get rid of B and C by making D more complex (the WITH clause might help to keep it overseeable).
Inserting data into D is (most likely) not possible per se, but you can create and INSTEAD OF INSERT trigger to handle that, i.e. insert into the fact table A instead.
Example for using the WITH clause:
Instead of
create view b as select * from dual;
create view c as select * from b;
create view d as select * from c;
you could write
create view d as
with b as (select * from dual),
c as (select * from b)
select * from c;
As you can see, the existing view definition goes 1:1 into the WITH clause, so it's not too difficult to create a view to combine all views.
If you are on Oracle 12c you might look at DBMS_UTILITY.EXPAND_SQL_TEXT, though you'll probably want to clean up the output a bit for readability.
A few things first
1) A view is a predefined sql query so it is not possible to insert records directly into it. Even a materialized view which is a persistant table structure only gets populated with the results of a query thus as things stand this is not possible. What is possible is to create a new table to populate the data which is currently aggregated at view D
2) It is very possible to aggregate data at muliple levels in Informatica using combination of multiple inline sorter and aggregater transformations which will generate the data at the level you're looking for.
3) Should you do it? Data warehousing best practices would say no and keep the data as granular as possible per the original table A so that it can be rolled up in many ways (refer Kimball group site and read up on star schema for such matters). Do you have much sway in the choice though?
4) The current process (while often used) is not that much better in terms of star schema
In my ASP.NET web app I have a DataTable filled with data to insert into tblChildren.
The DataTable is passed to a stored procedure.
In the SP I need to read each row (e.i Loop through the DataTable), change a couple of columns in it (with accordance to the relevant data in tblParents) and only then insert the row into tblChildren.
SqlBulkCopy wouldn't do and I don't think TVP will do either (not sure... not too familiar with it yet).
Of course I can iterate through the DataTable rows in the app and send each one separately to the SP, but that would mean hundreds of round trips to the SqlServer.
I came across two possibilities that might achieve that : (1) Temp table
(2) Cursor.
The first is quite messy and the second, as I understand it, is NOT recommended)
Any guidance would be much appreciated.
EDIT :
I tried the approach of user-defined Table Type.
That works because I populate the Table Type (TT_Children) with values in the TT_Child_Family_Id column.
In real life, though, I will not know these values and I would need to loop thru #my_TT_Children and for each row get the value from tblFamilies, something like this :
SELECT Family_Id FROM tblFamilies WHERE Family_Name = TT_Child_Last_Name
(assuming there is always an equivalent for TT_Child_Last_Name in tblFamilies.Family_Name)
So my question is - how to loop through the table-type and for each row look up a value in a different table?
EDIT 2 (the solution) :
As in Amir's perfect answer, the stored procedure should look like this :
ALTER PROCEDURE [dbo].[usp_Z_Insert_Children]
#my_TT_Children TT_Children READONLY
AS
BEGIN
INSERT INTO tblChildren(Child_FirstName,
Child_LastName,
Child_Family_ID)
SELECT Cld.tt_child_FirstName,
Cld.tt_child_LastNAme,
Fml.Family_Id FROM #my_TT_Children Cld
INNER JOIN tblFamilies fml
ON Cld.TT_Child_LastName = Fml.Family_Name
END
Notes by Amir : column Family_Name in tblFamily must be unique and preferably indexed.
(Also I noticed that in case TT_Child_LastName does not have a match in tblFamilies, the row will not be inserted and I'll never know about it. That means that I have to check somehow if all rows were successfully processed).
You can join tblFamilies into the insert in the procedure and take the value from there. Much more efficient than looping through.
Or create a cursor and do one child at a time.
1) Make sure there is only one occurance of FamilyName in tblFamilies.
2) Make sure that if tblFamilies is a large table, then the FamilyName column is indexed.
INSERT INTO tblChildren(Child_FirstName, Child_LastName, Child_Family_ID)
SELECT Cld.tt_child_FirstName,
Cld.tt_child_LastNAme,
Fml.FamilyID
FROM #my_TT_Children Cld
INNER JOIN tblFamilies fml on Cld.TT_Child_LastName = Fml.FamilyName
But be aware that if tblFamilies has more than one entry per Family_Name, then this will duplicate the data. In this case you will need to add more restrictions in the where.
I have two problem sets. What I am preferably looking for is a solution which combines both.
Problem 1: I have a table of lets say 20 rows. I am reading 150,000 rows from other table (say table 2). For each row read from table 2, I have to match it with a specific row of table 1 (not matching whole row, few columns. like if table2.col1 = table1.col && table2.col2 = table1.col2) etc. Is there a way that i can cache table 1 so that i don't have to query it again and again ?
Problem 2: I want to generate query string dynamically i.e., if parameter 2 is null then don't put it in where clause. Now the only option left is to use immidiate execute which will be very slow.
Now what i am asking that how can i have dynamic query to compare it with table 1 ? any ideas ?
For problem 1, as mentioned in the comments, let the database handle it. That's what it does really well. If it is something being hit often, then the blocks for the table should remain in the database buffer cache if the buffer cache is sized appropriately. Part of DBA tuning would be to identify appropriate sizing, pinning tables into the "keep" pool, etc. But probably not something that needs worrying over.
If the desire is just to simplify writing the queries rather than performance, then views or stored procs can simplify the repetitive use of the join.
For problem 2, a query in a format like this might work for you:
SELECT id, val
FROM myTable
WHERE filter = COALESCE(v_filter, filter)
If the input parameter v_filter is null, then just automatically match the existing column. This assumes the existing filter column itself is never null (since you can't use = for null comparisons). Also, it assumes that there are other indexed portions in the WHERE clause since a function like COALESCE isn't going to be able to take advantage of an index.
For problem 1 you just join the tables. If there is an equijoin and one table is quite small and the other large then you're likely to get a hash join. This is effectively a caching mechanism, and the total cost of reading the tables and performing the join is only very slightly higher than that of reading the tables (as long as the hash table fits in memory).
It does not make a difference if the query is constructed and run through execute immediate -- the RDBMS hash join will still act as an effective cache.
I have a bunch of different tables each of which have an ID column and I want to provide a search feature which will search all columns of all tables and return the ID column of a row that contains a matching string. Since I want to do this for all columns of all tables I cant do a WHERE col1 CONTAINS TEXT_STRING OR col2 .... Any ideas?
Well, if you need to do this, you have a problem with the design. but of course there are many times you have to use what other people put on you!
I would create a view, in the view I would create a union of all possible tables. Later you can just search the view. But you have to build index on that column on all the tables otherwise you will get very bad performance.