Oracle SQL - Comparing Rows - sql

I have a problem I'm working on with Oracle SQL that goes something like this.
TABLE
PurchaseID CustID Location
----1------------1-----------A
----2------------1-----------A
----3------------2-----------A
----4------------2-----------B
----5------------2-----------A
----6------------3-----------B
----7------------3-----------B
I'm interested in querying the Table to return all instances where the same customer makes a purchase in different locations. So, for the table above, I would want:
OUTPUT
PurchaseID CustID Location
----3------------2-----------A
----4------------2-----------B
----5------------2-----------A
Any ideas on how to accomplish this? I haven't been able to think of how to do it, and most of my ideas seem like they would be pretty clunky. The database I'm using has 1MM+ records, so I don't want it to run too slowly.
Any help would be appreciated. Thanks!

SELECT *
FROM YourTable T
WHERE CustId IN (SELECT CustId
FROM YourTable
GROUP BY CustId
HAVING MIN(Location) <> MAX(Location))

You should be able to use something similar to the following:
select purchaseid, custid, location
from yourtable
where custid in (select custid
from yourtable
group by custid
having count(distinct location) >1);
See SQL Fiddle with Demo.
The subquery in the WHERE clause is returning all custids that have a total number of distinct locations that are greater than 1.

In English:
Select a row if another row exists with the same customer and a different location.
In SQL:
SELECT *
FROM atable t
WHERE EXISTS (
SELECT *
FROM atable
WHERE CustID = t.CustID
AND Location <> t.Location
);

Here's one approach using a sub-query
SELECT T1.PurchaseID
,T1.CustID
,T1.Location
FROM YourTable T1
INNER JOIN
(SELECT T2.CustID
,COUNT (DISTINCT T2.Location )
FROM YourTable T1
GROUP BY
T2.CustID
HAVING COUNT (DISTINCT T2.Location )>1
) SQ
ON SQ.CustID = T1.CustID

This should only require one full table scan.
create table test (PurchaseID number, CustID number, Location varchar2(1));
insert into test values (1,1,'A');
insert into test values (2,1,'A');
insert into test values (3,2,'A');
insert into test values (4,2,'B');
insert into test values (5,2,'A');
insert into test values (6,3,'B');
insert into test values (7,3,'A');
with repeatCustDiffLocations as (
select PurchaseID, custid, location, dense_rank () over (partition by custid order by location) r
from test)
select b.*
from repeatCustDiffLocations a, repeatCustDiffLocations b
where a.r > 1
and a.custid = b.custid;

This makes most sense to me as I was trying to return the rows with the same values throughout the table, specifically for two columns as shown in this stackoverflow answer here.
The answer to your problem in this format is:
SELECT DISTINCT a.*
FROM TEST a
INNER JOIN TEST b
ON a.CUSTOMERID = b.CUSTOMERID AND
a.LOCATION <> b.LOCATION;
However, the solution to a problem such as mine with two columns having matching values in multiple rows (2 in this instance, would yield no results because all PurchaseID's are unique):
SELECT DISTINCT a.*
FROM TEST a
INNER JOIN TEST b
ON a.CUSTOMERID = b.CUSTOMERID AND
a.PURCHASEID = b.PURCHASEID AND
a.LOCATION <> b.LOCATION;
Although, this wouldn't return the correct results based on the what needs to be queried, it shows that the query logic works
SELECT DISTINCT a.*
FROM TEST a
INNER JOIN TEST b
ON a.CUSTOMERID = b.CUSTOMERID AND
a.PURCHASEID <> b.PURCHASEID AND
a.LOCATION = b.LOCATION;
If anyone wants to try in Oracle here is the table and values to insert:
CREATE TABLE TEST (
PurchaseID integer,
CustomerID integer,
Location varchar(1));
INSERT ALL
INTO TEST VALUES (1, 1, 'A')
INTO TEST VALUES (2, 1, 'A')
INTO TEST VALUES (3, 2, 'A')
INTO TEST VALUES (4, 2, 'B')
INTO TEST VALUES (5, 2, 'A')
INTO TEST VALUES (6, 3, 'B')
INTO TEST VALUES (7, 3, 'B')
SELECT * FROM DUAL;

Related

Update table with using NEWID() function

CREATE TABLE Products(Id INT, Name CHAR(100), DefaultImageId INT NULL);
INSERT INTO Products (Id, Name, DefaultImageId) VALUES(1, 'A', NULL);
INSERT INTO Products (Id, Name, DefaultImageId) VALUES(2, 'A', NULL);
INSERT INTO Products (Id, Name, DefaultImageId) VALUES(3, 'A', NULL);
INSERT INTO Products (Id, Name, DefaultImageId) VALUES(4, 'A', NULL);
INSERT INTO Products (Id, Name, DefaultImageId) VALUES(5, 'A', NULL);
INSERT INTO Products (Id, Name, DefaultImageId) VALUES(1, 'B', NULL);
INSERT INTO Products (Id, Name, DefaultImageId) VALUES(2, 'B', NULL);
INSERT INTO Products (Id, Name, DefaultImageId) VALUES(3, 'B', NULL);
In general, I would update a table randomly like the following scripts.
update a
set DefaultImageId=1
from Products as a
where name = 'A'
and id in (
select top 2 id
from Products as b
where a.name = b.name
order by newid()
)
However, I get some issue. It would update more/less then 2 rows. I try execute the following scripts many times for debug.
The results are not always only two records. If I remove the order by newid(), the number of output result will be fine. It seems like the problem in newid(). How can I solve this problem? Thanks
select *
from Products as a
where name = 'A'
and id in (
select top 2 id
from Products as b
where a.name = b.name
order by newid()
)
You can try this:
UPDATE U SET DefaultImageId = 1
FROM(
SELECT TOP 2 * FROM dbo.Products WHERE Name = 'A' ORDER BY NEWID()
) AS U
However if you filter out the DefaultImageId=1 in inner query, it would be even better too. You should note that in this case, the inner query might produce less than 2 records.
UPDATE U SET DefaultImageId = 1
FROM(
SELECT TOP 2 * FROM dbo.Products WHERE Name = 'A' AND DefaultImageId <> 1 ORDER BY NEWID()
) AS U
I think you're just looking for a simple JOIN as
UPDATE A
SET DefaultImageId = 1
FROM Products A
JOIN
(
SELECT TOP 2 Id, Name
FROM Products B
ORDER BY NEWID()
) B ON A.Id = B.Id AND A.Name = B.Name;
Demo
SQL SERVER BUG:
I think you just hit the following SQL server bug, that is still under review since 2018.
https://feedback.azure.com/forums/908035-sql-server/suggestions/33272404-cte-containing-top-and-newid-producing-incorrect
WORK AROUND:
The only thing that seems to work, based on my stesting, is if you eliminate the outside reference from the subquery, things works as expected. You can eliminate the outside reference by doing any of the following as suggested by others here.
Option 1: Keeping the query as-is, except hard-coding the filter criteria inside the subquery instead of using an outside reference:
SELECT ...
FROM t1
WHERE
t1.id IN (SELECT TOP 2 id subquery with NO outside reference WHERE name = 'A' ORDER BY ORDER BY newid() )
AND t1.name = 'A'
....
Option 2: Using a JOIN with proper ON matching criteria instead of using an outside reference inside the subquery:
SELECT ...
FROM t1
INNER JOIN (SELECT TOP 2 id subquery with NO outside reference ORDER BY newid() ) t2 ON t1.id = t2.id and t1.name = t2.name
WHERE
t1.name = 'A'
....
OR
DETAILS:
Based on my testing, when all of the following are aligned, the SQL Engine returns an unpredictable result:
An outside table is referenced inside the subquery (or through CTE) as a filtering criteria
NEWID() function is used in the ORDER BY with a SELECT TOP statement (It does NOT happen with other non-deterministic functions like RAND() for example. Something specific to NEWID())
The SELECT TOP statement uses a number less than the number of records exists in that table
This seems to throw off the SQL engine. The result changes at every run: The number of records returned varies at every run between zero records and max number of records that would have been returned if SELECT TOP was not used.
The query plan does not make sense at all. See the screenshot here. TOP N returns more than it is supposed to and id IN () predicate returns random number of records (zero in this example). How could that be possible? I have no clue. Also, note that the id IN () predicate is applied before the left table scan, which shouldn't happen. I think the unreliable result has something to do with that. We'll have to wait until SQL Server team fixes the bug to be able write the queries the way you wrote.
As you are using Correlated subquery, where outer query name is referred by subquery name, you are getting different result set.
I would suggest you to use the name parameter twice to avoid this problem.
select *
from Products as a
where name='A'
and id in
(select top 2 id
from Products as b
where b.name = 'A'
order by newid())
Similarly, for update also, you change the query
update a
set DefaultImageId=1
from Products as a
where name = 'A'
and id in (
select top 2 id
from Products as b
where b.name = 'A'
order by newid()
)
Something like this
This query selects 2 rows at random.
with top_2_cte(id, [name]) as (
select top 2 id, [name]
from Products
where name='A'
order by newid())
select a.*
from Products a
join
top_2_cte ttc on a.Id=ttc.id
and a.[Name]=ttc.[name];
Update statement
;with top_2_cte(id, [name]) as (
select top 2 id, [name]
from Products
where name='A'
order by newid())
update a
set DefaultImageId=1
from Products a
join
top_2_cte ttc on a.Id=ttc.id
and a.[Name]=ttc.[name];

How To Query IDs that have different Patient Names

First, I understand that I should have a Primary Key on a Value Patient ID. A project was performed for ID conversions that did not go very well. So now I need to find all Patient IDs that have differnt Patient Names. There are 4 different DBs>Tables that contain info. For now I selected them into a Temp DB. Because I actually need all PIDs to be distinct across those DBs. Our application has tools to keep that synchronized. But due to some bad SQL work, I need to synchronize all the Data again.
PID NAME
1234 Johnson
1234 Johnson
4567 Jones
4567 Alexander
I am trying to write a query that will return the results of PID 4567 + NAME Values of Jones and Alexander.
You need to use a self-join. Please try this:
create table #temp
(id int, name varchar(30))
insert into #temp values (1,'johnson')
insert into #temp values (1,'johnson')
insert into #temp values (2,'james')
insert into #temp values (2,'Alex')
SELECT * FROM #temp WHERE id IN (
SELECT a.id FROM #temp a
JOIN #temp b on b.id = a.id AND b.name <> a.name
)
SELECT Min(PID), Name FROM [Table]
GROUP BY Name
HAVING Count(PID) = 1
SELECT PID,NAME
FROM TABLE
GROUP BY PID,NAME
HAVING COUNT(*) =1
I think this will do it
select p.pid, max(name), min(name), count(*) as cnt
from p
group by pid
having max(name) <> min(name)
or
select p1.pid, p1.name, p2.name
from p p1
join p p2
on p1.pid = p2.pid
and p1.name < p2.name
order by p1.pid, p1.name, p2.name
There are a lot of ways and some more optimized than others depending on which RDBMS system you are using. But typically this is a 2 step operations.
1) Find all of the PIDs that have more than 1 Name associated with it
2) Relate back to get the rest of the data you are seeking.
CREATE TABLE #T (
PID INT
,Name VARCHAR(25)
)
INSERT INTO #T (PID,Name) VALUES (1234,'Johnson'),(1234,'Johnson'),(4567,'Jones'),(4567,'Alexander')
SELECT
t2.*
FROM
(
SELECT
PID
FROM
#T t1
GROUP BY
PID
HAVING COUNT(DISTINCT Name) > 1
) dupes
INNER JOIN #T t2
ON dupes.PID = t2.PID
It is important when using a method such as the join or IN above that you use DISTINCT name because simplying counting * or name will return multiple occurrences of the same PID to name combination not simply duplicates.
If you only want the duplicate not all of the combinations. Using a RowNumber() or something can help you get to the answer a little more efficiently too. Or you can also use a method such as looking for existence of a non identical record, like so:
SELECT DISTINCT t1.PID, t1.Name
FROM
#T t1
WHERE
EXISTS (SELECT 1 FROM #t t2 WHERE t1.PID = t2.PID AND t1.Name <> t2.Name)
This way could perform faster for you depending on data sets etc. I would tend to stay away from solutions that use IN for cases like these.

How to use a bunch of clause in a having statement. Oracle

assume i have a query like this:
SELECT table1.id
FROM (
SELECT id, sum(column) as A
FROM table1
GROUP BY id
) a1
Left join (
SELECT id,
sum(column) as B
FROM table 2
GROUP BY Id
) a2
on table1.id=table2.id
.
.
.
.
Left join (
SELECT id, sum(column) as G
FROM table 7
GROUP BY id
) g1
on table1.id=table7.id
Having or where A+B - (C+D+E+F+G) >0
I tried both, none works.
Having return error on there's no group by in the first select and where doesn't return any rows.
First your question have some issues.
I'm going to guess you mean put alias a, b, c, d ....
instead of a1, a2, g1.
Also your left join should be something like a.id = b.id at the moment you create a subquery you have to use the alias instead of tablename.
If you fix that you should add a WHERE, I also guess you mean use the SUM() result
WHERE a.A + b.B - (c.C+ d.D+ e.E+ f.F+ g.G) > 0
.
SELECT a.id
FROM (
SELECT id, sum(column) as sumA
FROM table1
GROUP BY id
) a
Left join
(
SELECT id, sum(column) as sumB
FROM table 2
GROUP BY Id
) b
on a.id = b.id
.
.
.
.
Left join
(
SELECT id, sum(column) as sumG
FROM table 2
GROUP BY id
) g
on f.id = g.id
WHERE a.sumA + b.sumB - (c.sumC + d.sumD + e.sumE + f.sumF + g.sumG) >0
Juan has the right answer. I am just adding a SQLFiddle to help strengthen his answer. Please look at a smaller instance of the same solution here: http://sqlfiddle.com/#!4/81c275/1
Tables
create table table1(id int, col int);
insert into table1 values (1, 10);
insert into table1 values (2, 20);
insert into table1 values (2, 30);
create table table2(id int, col int);
insert into table2 values (1, 5);
insert into table2 values (2, 3);
insert into table2 values (2, 2);
create table table3(id int, col int);
insert into table3 values (1, 100);
insert into table3 values (2, 20);
insert into table3 values (2, 3);
SQL
select a1.id
from (select id, sum(col) as A from table1 group by id) a1
left join (select id, sum(col) as B from table2 group by id) a2
on a1.id = a2.id
left join (select id, sum(col) as C from table3 group by id) a3
on a1.id = a3.id
where A + B - (C) > 0
You can add more tables in the SQLFiddle with whatever values you please, and change the SQL accordingly by appending D, E, F, G etc after C in (C).
The above example will result in output of 2 since ID 2's A+B = 55 and C = 23. A+B-C > 0 for this record and therefore the output will be 2.
I believe that you need to take out the 'where' and move it up if you still need it.
So that it would look something like this,
select table1.id from(
...
...
...)
Having ((A+B)-(C+D+R+F+G)>0)
According to this site:
http://www.w3schools.com/sql/sql_having.asp

Find Modified/New/Deleted Records Between Two Tables

I want to find new, modified and deleted records in one table (tableA) by comparing it to another table (tableB). Both tables are of the same schema and has a unique ID field.
In my situation, tableA is originally the same as tableB but it has been edited by some external organisation and once they have done their edits, they send the table back via ZIP file, and we re-populate (truncate and insert) that data to tableA. So I want to find out what records have changed in tableA. I am using SQL Server 2012.
I can get new and modified records with the "except" keyword:
select * from tableA
except
select * form tableB
(Let's call the above results ResultsA)
I can also get deleted and modified records:
select * from tableB
except
select * form tableA
(Let's call the above results ResultsB)
The problem is, both ResultsA and ResultsB have the same records that have been modified/edited. So the modified/edited records are doubled up. I can use inner join or intersect on ResultsA and ResultsB to get just the modified records (call this results ResultsC). But then I will need to use join/except again between ResultsA and ResultsC to get just the new records, and join/except again between ResultsB and ResultsC to get just the deleted records... I tried this and this but they are not working for me.
Obviously this is not good. Are there any elegant and simpler ways to find out the records that have been deleted, modified or added in tableA compared to tableB?
How about:
-- DELETED
SELECT B.*, 'DELETED' AS 'CHANGE_TYPE'
FROM TableB B
LEFT JOIN TableA A ON B.PK_ID = A.PK_ID
WHERE A.PK_ID IS NULL
UNION
-- NEW
SELECT A.*, 'NEW' AS 'CHANGE_TYPE'
FROM TableA A
LEFT JOIN TableB B ON B.PK_ID = A.PK_ID
WHERE B.PK_ID IS NULL
UNION
-- MODIFIED
SELECT B.*, 'MODIFIED' AS 'CHANGE_TYPE'
FROM (
SELECT * FROM TableA
EXCEPT
SELECT * FROM TableB
) S1
INNER JOIN TableB B ON S1.PK_ID = B.PK_ID;
Not exactly elegant, but it works.
Based on what i understood i came up with the following solution.
DECLARE #tableA TABLE (ID INT, Number INT)
DECLARE #tableB TABLE (ID INT, Number INT)
INSERT INTO #tableA VALUES
(1,10),
(2,20),
(3,30),
(4,40)
INSERT INTO #tableB VALUES
(1,11),
(2,20),
(4,40),
(5,50)
SELECT *,'Modified or deleted' as 'Status' FROM
(
select * from #tableA
except
select * from #tableB
)a WHERE ID NOT IN
(
select ID from #tableB
except
select ID from #tableA
)
UNION
SELECT *,'New' as 'Status' FROM
(
select * from #tableB
except
select * from #tableA
)b WHERE ID NOT IN
(
SELECT ID FROM
(
select * from #tableA
except
select * from #tableB
)a WHERE ID NOT IN
(
select ID from #tableB
except
select ID from #tableA
)
)
You can use the OUTPUT clause:
Returns information from, or expressions based on, each row affected by an INSERT, UPDATE, or DELETE statement. These results can be returned to the processing application for use in such things as confirmation messages, archiving, and other such application requirements. Alternatively, results can be inserted into a table or table variable.
See the the following, sorry I don't have a practical code for you. But note the SQL output clause can be used to return any value from ‘inserted’ and ‘deleted’ (New value and Old value) tables when doing an insert or update. follow this for more info
declare #DBOrderItem table
(
OrderItemGuid UniqueIdentifier default newid(),
Name VarChar(100)
);
declare #PayloadOrderItem table
(
OrderItemGuid UniqueIdentifier default newid(),
Name VarChar(100)
);
insert into #DBOrderItem (Name) values ('Phone');
insert into #DBOrderItem (Name) values ('Laptop');
insert into #PayloadOrderItem
select top 1 * from #DBOrderItem;
insert into #PayloadOrderItem (Name) values ('Tablet');
select doi.OrderItemGuid,
doi.Name,
case when poi.OrderItemGuid is null then 'Delete' else 'Update' end ActionType
from #DBOrderItem doi
left join #PayloadOrderItem poi on doi.OrderItemGuid = poi.OrderItemGuid
union
select poi.OrderItemGuid,
poi.Name,
'Add' ActionType
from #PayloadOrderItem poi
left join #DBOrderItem doi on doi.OrderItemGuid = poi.OrderItemGuid
where doi.OrderItemGuid is null;
Another solution that works quite efficiently is to use a where not exists an intersect between the two tables. Its very compact.
SELECT
IsNull(tableB.ID,tableA.ID) as 'ID',
IsNull(tableB.Number,tableA.Number) as 'Number',
'Action' = CASE
WHEN tableB.ID IS NULL THEN 'Deleted'
WHEN tableA.ID IS NULL THEN 'Created'
ELSE 'Updated'
END
FROM tableA
FULL OUTER JOIN tableB
ON tableB.ID = tableA.ID
WHERE
NOT EXISTS (SELECT tableB.* INTERSECT SELECT tableA.*)
This keeps the table scans down to a minimum, and provides detection of new, deleted and changed records.
I put all three from here into fiddle, and its surprising how differently they all compile.
http://sqlfiddle.com/#!6/b1a5a/5
This one works without primary key also a bit more elegant .(in my opinion!)
WITh A AS (SELECT 1,2,3 FROM DUAL
UNION ALL
SELECT 1,3,2 FROM DUAL
UNION ALL
SELECT 1,3,1 FROM DUAL),
B AS (SELECT 1,3,2 FROM DUAL
UNION ALL
SELECT 1,2,3 FROM DUAL
UNION ALL
SELECT 1,3,5 FROM DUAL
)
,
C AS
(SELECT * FROM A
MINUS
SELECT * FROM B
),
D AS( SELECT * FROM b
MINUS
SELECT * FROM A)
SELECT C.* ,'Deleted' FROM c
UNION ALL
SELECT D.* ,'Added' FROM D

Join to only the "latest" record with t-sql

I've got two tables. Table "B" has a one to many relationship with Table "A", which means that there will be many records in table "B" for one record in table "A".
The records in table "B" are mainly differentiated by a date, I need to produce a resultset that includes the record in table "A" joined with only the latest record in table "B". For illustration purpose, here's a sample schema:
Table A
-------
ID
Table B
-------
ID
TableAID
RowDate
I'm having trouble formulating the query to give me the resultset I'm looking for any help would be greatly appreciated.
SELECT *
FROM tableA A
OUTER APPLY (SELECT TOP 1 *
FROM tableB B
WHERE A.ID = B.TableAID
ORDER BY B.RowDate DESC) as B
select a.*, bm.MaxRowDate
from (
select TableAID, max(RowDate) as MaxRowDate
from TableB
group by TableAID
) bm
inner join TableA a on bm.TableAID = a.ID
If you need more columns from TableB, do this:
select a.*, b.* --use explicit columns rather than * here
from (
select TableAID, max(RowDate) as MaxRowDate
from TableB
group by TableAID
) bm
inner join TableB b on bm.TableAID = b.TableAID
and bm.MaxRowDate = b.RowDate
inner join TableA a on bm.TableAID = a.ID
table B join is optional: it depends if there are other columns you want
SELECT
*
FROM
tableA A
JOIN
tableB B ON A.ID = B.TableAID
JOIN
(
SELECT Max(RowDate) AS MaxRowDate, TableAID
FROM tableB
GROUP BY TableAID
) foo ON B.TableAID = foo.TableAID AND B.RowDate= foo.MaxRowDate
With ABDateMap AS (
SELECT Max(RowDate) AS LastDate, TableAID FROM TableB GROUP BY TableAID
),
LatestBRow As (
SELECT MAX(ID) AS ID, TableAID FROM ABDateMap INNER JOIN TableB ON b.TableAID=a.ID AND b.RowDate = LastDate GROUP BY TableAID
)
SELECT columns
FROM TableA a
INNER JOIN LatestBRow m ON m.TableAID=a.ID
INNER JOIN TableB b on b.ID = m.ID
Just for the clarity's sake and to benefit those who will stumble upon this ancient question. The accepted answer would return duplicate rows if there are duplicate RowDate in Table B. A safer and more efficient way would be to utilize ROW_NUMBER():
Select a.*, b.* -- Use explicit column list rather than * here
From [Table A] a
Inner Join ( -- Use Left Join if the records missing from Table B are still required
Select *,
ROW_NUMBER() OVER (PARTITION BY TableAID ORDER BY RowDate DESC) As _RowNum
From [Table B]
) b
On b.TableAID = a.ID
Where b._RowNum = 1
Try using this:
BEGIN
DECLARE #TB1 AS TABLE (ID INT, NAME VARCHAR(30) )
DECLARE #TB2 AS TABLE (ID INT, ID_TB1 INT, PRICE DECIMAL(18,2))
INSERT INTO #TB1 (ID, NAME) VALUES (1, 'PRODUCT X')
INSERT INTO #TB1 (ID, NAME) VALUES (2, 'PRODUCT Y')
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (1, 1, 3.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (2, 1, 4.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (3, 1, 5.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (1, 2, 0.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (2, 2, 1.99)
INSERT INTO #TB2 (ID, ID_TB1, PRICE) VALUES (3, 2, 2.99)
SELECT A.ID, A.NAME, B.PRICE
FROM #TB1 A
INNER JOIN #TB2 B ON A.ID = B.ID_TB1 AND B.ID = (SELECT MAX(ID) FROM #TB2 WHERE ID_TB1 = A.ID)
END
This will fetch the latest record with JOIN. I think this will help someone
SELECT cmp.*, lr_entry.lr_no FROM
(SELECT * FROM lr_entry ORDER BY id DESC LIMIT 1)
lr_entry JOIN companies as cmp ON cmp.id = lr_entry.company_id