Automatic index on a table - sql-server

I was trying to find out if any index is created on a table, automatically. I used this link to find indexes on Employees table which has 4 columns, namely empid, empName, salary and dept. The query shows that table already has an index - I don't have keys on the table. This link says that non-clustered indexes are not created automatically on a table. So what is the type of index that is already present on Employees table ? Also, index name is shown as null.
Create statement and query used to find indexes on Employees table are as follows :
Create table Employees(
empid int,
empName varchar(100),
salary int,
dept varchar(10)
)
select * from sys.indexes
where object_id = (select object_id
from sys.objects where name = 'Employees')

This is the output from the sys.indexes query in your question:
+------------+------+----------+------+-----------+-----------+---------------+----------------+----------------+----------------------+-------------+-----------+-------------+-----------------+----------------------------+-----------------+------------------+------------+-------------------+-------------------+---------------------------+--------------+
| object_id | name | index_id | type | type_desc | is_unique | data_space_id | ignore_dup_key | is_primary_key | is_unique_constraint | fill_factor | is_padded | is_disabled | is_hypothetical | is_ignored_in_optimization | allow_row_locks | allow_page_locks | has_filter | filter_definition | compression_delay | suppress_dup_key_messages | auto_created |
+------------+------+----------+------+-----------+-----------+---------------+----------------+----------------+----------------------+-------------+-----------+-------------+-----------------+----------------------------+-----------------+------------------+------------+-------------------+-------------------+---------------------------+--------------+
| 1746105261 | NULL | 0 | 0 | HEAP | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 | NULL | NULL | 0 | 0 |
+------------+------+----------+------+-----------+-----------+---------------+----------------+----------------+----------------------+-------------+-----------+-------------+-----------------+----------------------------+-----------------+------------------+------------+-------------------+-------------------+---------------------------+--------------+
Note the type_desc value is "HEAP", meaning a table without a clustered index. So the returned row is the table itself rather than an index. The documentation for sys.indexes states (my emphasis):
Contains a row per index or heap of a tabular object, such as a table,
view, or table-valued function

Related

SSIS Lookup Multiple Columns in one table to the same ID column in another

I have the following table:
EventValue | Person1 | Person2 | Person3 | Person4 | Meta1 | Meta2
-------------------------------------------------------------------------------------------
123 | joePerson01 | samRock01 | nancyDrew01 | steveRogers01 | 505 | 606
321 | steveRogers02 | yoMama01 | ruMo01 | lukeJedi01 | 707 | 808
I want to transform the Person columns into IDs for my destination table, so all of the ID's would be coming from the same Person table in my Destination DB:
ID | FirstName | LastName | DatabaseOneID | DatabaseTwoID
----------------------------------------------------------
1 | Joe | Person | joePerson01 | personJoe01
2 | Sam | Rockwell | samRock01 | rockSam01
3 | Nancy | Drew | nancyDrew01 | drewNancy01
4 | Steve | Rogers | steveRogers01 | rogersSteve01
5 | Steve R | Rogers | steveRogers02 | rogersSteve02
6 | Yo | Mama | yoMama01 | mamaYo01
7 | Rufus | Murdock | ruMo01 | moRu01
8 | Luke | Skywalker | lukeJedi01 | jediLuke01
With results like so:
MetaID | EventValue | Person1ID | Person2ID | Person3ID | Person4ID
------------------------------------------------------------------------
1 | 123 | 1 | 2 | 3 | 4
2 | 321 | 5 | 6 | 7 | 8
I currently have a Lookup Transform looking up the first Person column, but couldn't figure out how to convert all 4 Person columns into IDs within the same lookup.
You could do it in one query, or use UNPIVOT, or use a scalar function if you think it'll be more fixable for your implementation. Then, you just create a view of it, in which it'll be an easy access for you.
here is a quick example :
DECLARE
#tb1 TABLE
(
EventValue INT
, Person1 VARCHAR(250)
, Person2 VARCHAR(250)
, Person3 VARCHAR(250)
, Person4 VARCHAR(250)
, Meta1 INT
, Meta2 INT
)
DECLARE
#Person TABLE
(
ID INT
, FirstName VARCHAR(250)
, LastName VARCHAR(250)
, DatabaseOneID VARCHAR(250)
, DatabaseTwoID VARCHAR(250)
)
INSERT INTO #tb1
VALUES
(123,'joePerson01','samRock01','nancyDrew01','steveRogers01',505,606),
(321,'steveRogers02','yoMama01','ruMo01','lukeJedi01',707,808)
INSERT INTO #Person
VALUES
(1,'Joe','Person','joePerson01','personJoe01'),
(2,'Sam','Rockwell','samRock01','rockSam01'),
(3,'Nancy','Drew','nancyDrew01','drewNancy01'),
(4,'Steve','Rogers','steveRogers01','rogersSteve01'),
(5,'SteveR','Rogers','steveRogers02','rogersSteve02'),
(6,'Yo','Mama','yoMama01','mamaYo01'),
(7,'Rufus','Murdock','ruMo01','moRu01'),
(8,'Luke','Skywalker','lukeJedi01','jediLuke01')
SELECT ROW_NUMBER() OVER(ORDER BY EventValue) AS MetaID, *
FROM (
SELECT
t.EventValue
, MAX(CASE WHEN t.Person1 IN(p.DatabaseOneID, p.DatabaseTwoID) THEN p.ID ELSE NULL END) AS Person1ID
, MAX(CASE WHEN t.Person2 IN(p.DatabaseOneID, p.DatabaseTwoID) THEN p.ID ELSE NULL END) AS Person2ID
, MAX(CASE WHEN t.Person3 IN(p.DatabaseOneID, p.DatabaseTwoID) THEN p.ID ELSE NULL END) AS Person3ID
, MAX(CASE WHEN t.Person4 IN(p.DatabaseOneID, p.DatabaseTwoID) THEN p.ID ELSE NULL END) AS Person4ID
FROM #tb1 t
LEFT JOIN #Person p
ON p.DatabaseOneID IN(t.Person1, t.Person2, t.Person3, t.Person4)
OR p.DatabaseTwoID IN(t.Person1, t.Person2, t.Person3, t.Person4)
GROUP BY t.EventValue
) D
I currently have a Lookup Transform looking up the first Person column, but couldn't figure out how to convert all 4 Person columns into IDs within the same lookup.
You cannot do this within the same lookup, you have to add a Lookup Transformation for each Column. In your case you should add 4 Lookup Transformation.
If source database and destination database are on the same server, then you can use a SQL query to achieve that as mentioned in the other answer, but in case that each database is on a separate server you have to go with Lookup transformation or you have to import data into a staging table and perform Join operations using SQL.

Does Oracle's Optimizer Take into Consideration a Subquery's Underlying Columns?

Say you have a query like so:
with subselect as (
select foo_id
from foo
)
select bar_id
from bar
join subselect on foo_id = bar_id
where foo_id = 1000
Imagine you have an index on foo_id. Is Oracle's database smart enough to use the index in the query for the line "where foo_id = 1000"? OR since foo_id is wrapped in a subquery, does Oracle lose the index information related to this column?
Perform a simple test:
create table foo as
select t.object_id as foo_id, t.* from all_objects t;
create table bar as
select t.object_id as bar_id, t.* from all_objects t;
create index foo_id_ix on foo(foo_id);
exec dbms_stats.GATHER_TABLE_STATS(ownname=>user, tabname=>'FOO', method_opt=>'FOR ALL INDEXED COLUMNS' );
explain plan for
with subselect as (
select foo_id
from foo
)
select bar_id
from bar
join subselect on foo_id = bar_id
where foo_id = 1000;
select * from table( DBMS_XPLAN.DISPLAY );
and a result of last query is:
Plan hash value: 445248211
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 10 | 366 (1)| 00:00:01 |
| 1 | MERGE JOIN CARTESIAN| | 1 | 10 | 366 (1)| 00:00:01 |
|* 2 | TABLE ACCESS FULL | BAR | 1 | 5 | 365 (1)| 00:00:01 |
| 3 | BUFFER SORT | | 1 | 5 | 1 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN | FOO_ID_IX | 1 | 5 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("BAR_ID"=1000)
4 - access("FOO_ID"=1000)
In the above example Oracle uses |* 4 | INDEX RANGE SCAN using index: FOO_ID_IX for filter 4 - access("FOO_ID"=1000)
So the answer is:
yes, the Oracle's database is smart enough to use the index in the query for the line "where foo_id = 1000"

Fetching multiple rows by foreign key column

I have an Address table that contains multiple addresses
Address:
| Id | details | ... |
- - - - - - - - - - - - - - - -
| 1 | details | ... |
| 2 | details | ... |
| 3 | details | ... |
| 4 | details | ... |
Then i have a site table, that contains address.id as FK to represent Billing or Shipping address,
Site:
| Id | details | Ship_Address_id | Bill_Address_id | ... |
- - - - - - - - - - - - - - - -
| 1 | details | 1 | 2 | |
| 2 | details | 1 | 3 | |
| 3 | details | 4 | 4 | |
| 4 | details | 2 | 3 | |
is there a way to join both the tables so that single row from site table may fetch two rows of address table, even in the case of same address of both columns.
i was expecting to use multiple join of site with address but that is not working:
This is what i tried:
SELECT CASE
WHEN ship.CRM_Address_Internal_Id_Ship IS NOT NULL THEN 'Ship'
WHEN bill.CRM_Address_Internal_Id_Ship IS NOT NULL THEN 'Bill'
ELSE '' END
FROM Address as Adr
LEFT JOIN Site ship ON ship.Ship_Address_id = Adr.id
LEFT JOIN Site bill ON bill.Bill_Address_id = Adr.id
Even if i don't use the CASE it does not fetch two rows for each address with respect to site.
Please advise.
EDIT
Here is the desired output table:
as in Site table we have two different ids of address table (Ship/Bill) so the result table should display two rows for each site.id
for example i'm fetching records for site.id 1 and 3 then
Result_Table:
| Address.ID | Address Details | TYPE | ... |
--------------------------------------------
| 1 | Other Details | Ship | ... |
| 2 | Other Details | Bill | ... |
| 4 | Other Details | Ship | ... |
| 4 | Other Details | Bill | ... |
There are various ways to do this. This is an example that I implemented by UNPIVOT, you can take a look as references:
declare #address table(id int, details nvarchar(100))
declare #site table(id int, details nvarchar(100), ship_address_id int, bill_address_id int)
insert into #address values(1,'details 1')
insert into #address values(2,'details 2')
insert into #address values(3,'details 3')
insert into #address values(4,'details 4')
insert into #site values(1,'details 1',1,2)
insert into #site values(2,'details 1',1,3)
insert into #site values(3,'details 1',4,4)
insert into #site values(4,'details 1',2,5)
SELECT *
FROM (
SELECT s.id as site_id, s.bill_address_id, s.ship_address_id
FROM #Site AS s
INNER JOIN #Address AS sa ON s.Ship_Address_id = sa.Id
INNER JOIN #Address AS ba ON s.Bill_Address_id = ba.Id
WHERE s.id in (1,3)) p
UNPIVOT
(address_id FOR address_type IN
(Ship_Address_id, Bill_Address_id)
) AS unpvt
Output
site_id address_id address_type
1 1 ship_address_id
1 2 bill_address_id
3 4 ship_address_id
3 4 bill_address_id
It looks as though you're looking for UNION ALL, and also that your tables are backwards. I think you're looking for something more like this:
SELECT ship.id Site_id, CASE
WHEN ship.CRM_Address_Internal_Id_Ship IS NOT NULL THEN 'Ship'
ELSE 'No Ship' END AddressPresent
FROM Site ship
LEFT JOIN Address Adr ON ship.Ship_Address_id = Adr.id
UNION ALL
SELECT bill.id Site_id, CASE
WHEN bill.CRM_Address_Internal_Id_Ship IS NOT NULL THEN 'Bill'
ELSE 'No Bill' END AddressPresent
FROM Site bill
LEFT JOIN Address Adr ON bill.Bill_Address_id = Adr.id
I'm also wondering if you have a 'CRM_Address_Internal_Id_Bill' column that should be in the second part of the query?

PostgreSQL conditional join - if column is not NULL

I have a table "temp"
author | title | bibkey | Data
-----------------------------------
John | JsPaper | John2008 | 65
Kate | KsPaper | | 60
| | Data2015 | 80
From this I want to produce two tables, a 'sample_table' and a 'ref_table' like so:
sample_table:
sample_id|ref_id| data
--------------------------
1 | 1 | 65
2 | 2 | 60
3 | 3 | 80
ref_table:
ref_id | author | title | bibkey
--------------------------------------
1 | John | JsPaper | John2008
2 | Kate | KsPaper |
3 | | | Data2015
I've created both tables
CREATE TABLE ref_table ( CREATE TABLE sample_table (
ref_id serial PRIMARY KEY, sample_id serial PRIMARY KEY,
author text, ref_id integer REFERENCES ref_table(ref_id),
title text, data numeric
bibkey text );
);
And inserted the unique author,title,bibkey rows into the reference table as above. What I want to do now is do the join for the sample_table to get the ref_id's. For my insert statement i currently have:
INSERT INTO sample_table (
ref_id,data
)
SELECT ref.ref_id, t.data
FROM
temp t
LEFT JOIN
ref_table ref ON COALESCE(ref.author,'00000') = COALESCE(t.author,'00000')
AND COALESCE(ref.title,'00000') = COALESCE(t.title,'00000')
AND COALESCE(ref.bibkey,'00000') = COALESCE(t.bibkey,'00000');
However i really want to have a conditional statement in the join, rather than all 3 like I have:
IF a bibkey exists for that row, I know it is unique, and join only on that.
If bibkey is NULL, then join on both author and title for the unique pair, and not bibkey.
Is this possible?

MySQL I want to optimize this further

So I started off with this query:
SELECT * FROM TABLE1 WHERE hash IN (SELECT id FROM temptable);
It took forever, so I ran an explain:
mysql> explain SELECT * FROM TABLE1 WHERE hash IN (SELECT id FROM temptable);
+----+--------------------+-----------------+------+---------------+------+---------+------+------------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-----------------+------+---------------+------+---------+------+------------+-------------+
| 1 | PRIMARY | TABLE1 | ALL | NULL | NULL | NULL | NULL | 2554388553 | Using where |
| 2 | DEPENDENT SUBQUERY | temptable | ALL | NULL | NULL | NULL | NULL | 1506 | Using where |
+----+--------------------+-----------------+------+---------------+------+---------+------+------------+-------------+
2 rows in set (0.01 sec)
It wasn't using an index. So, my second pass:
mysql> explain SELECT * FROM TABLE1 JOIN temptable ON TABLE1.hash=temptable.hash;
+----+-------------+-----------------+------+---------------+----------+---------+------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------+------+---------------+----------+---------+------------------------+------+-------------+
| 1 | SIMPLE | temptable | ALL | hash | NULL | NULL | NULL | 1506 | |
| 1 | SIMPLE | TABLE1 | ref | hash | hash | 5 | testdb.temptable.hash | 527 | Using where |
+----+-------------+-----------------+------+---------------+----------+---------+------------------------+------+-------------+
2 rows in set (0.00 sec)
Can I do any other optimization?
You can gain some more speed by using a covering index, at the cost of extra space consumption. A covering index is one which can satisfy all requested columns in a query without performing a further lookup into the clustered index.
First of all get rid of the SELECT * and explicitly select the fields that you require. Then you can add all the fields in the SELECT clause to the right hand side of your composite index. For example, if your query will look like this:
SELECT first_name, last_name, age
FROM table1
JOIN temptable ON table1.hash = temptable.hash;
Then you can have a covering index that looks like this:
CREATE INDEX ix_index ON table1 (hash, first_name, last_name, age);

Resources