Related
I am using a simple if exists statement in my stored procedure, but in output it is not printing the pseudo column values if the records does not exist.
SQL Server 2012
CREATE PROCEDURE test1
#Empid NVARCHAR(20)
AS
BEGIN
IF EXISTS (SELECT TOP 1 * FROM employees WHERE id = #Empid)
BEGIN
SELECT id, name, salary, 'Newemp' AS status, 1 AS Code
FROM employees
END
IF NOT EXISTS (SELECT TOP 1 * FROM employees WHERE id = #Empid)
BEGIN
SELECT id, name, salary, 'Oldemp' AS status1, 0 AS Code1
FROM employees
END
END
Result:
If record exists, this is returned as expected
ID Name Salary Status Code
-------------------------------
123 kkr 1000 Newemp 1
If the record doesn't exist - this is the problem:
Id Name Salary Status1 Code1
-----------------------------
Desired value:
ID Name Salary Status1 Code1
----------------------------
Oldemp 0
If the records doesn't exist, it is not printing the pseudo column values. I have changed the column names and executed to make sure that its taking the correct columns, and yes it is taking the correct ones, but failing to print the values.
Please help!
I did it myself, but Big thanks for your answers.
CREATE PROCEDURE test1
#Empid NVARCHAR(20)
AS
BEGIN
Create #temp
(Id Nvarchar(100),
Name Nvarchar(100),
Salary Nvarchar(100),
Status Nvarchar(100),
Code Nvarchar(100)
)
IF EXISTS (SELECT TOP 1 * FROM employees WHERE id = #Empid)
BEGIN
insert into #temp (id, name, salary)
values (SELECT id, name, salary
FROM employees WHERE id = #Empid)
Insert into # temp (Status , Code)
('Newemp', 1)
END
IF NOT EXISTS (SELECT TOP 1 * FROM employees WHERE id = #Empid)
BEGIN
insert into #temp (id, name, salary)
values (SELECT id, name, salary
FROM employees WHERE id = #Empid)
Insert into # temp (Status , Code)
('Oldemp', 0)
END
Select * from #temp
END
These queries are based on the number of records in employees - it could be one, it could be none, and it could be many. If you always want to get a single row, you can omit the from clause and just select literals:
SELECT NULL AS id, NULL AS name, NULL AS salary, 'Oldemp' ASs status1, 0 AS Code1
I'm trying to fetch a random row from a table in MySQL. I'm using the following query:
SELECT *
FROM my_table
WHERE id IN (
SELECT ROUND((RAND() * (MAX(id)-MIN(id)))+MIN(id)) AS id
FROM my_table
);
I wish to understand why that query sometimes returns more than 1 row when the inner query always get 1 row?
Thanks
Just to give you a hint, this will always return 1 random row so refactor your query.
Note: Your query will be very expensive if you have many rows in DB.
SELECT * FROM table
WHERE column = 'data'
ORDER BY RAND()
LIMIT 1
use this instead
SELECT *
FROM my_table
WHERE id = (
SELECT ROUND((RAND() * (MAX(id)-MIN(id)))+MIN(id)) AS id
FROM my_table
);
I ended with the following stored procedure
BEGIN
SET #indx=0;
DROP TEMPORARY TABLE IF EXISTS temp;
CREATE TEMPORARY TABLE temp
SELECT (#indx := #indx+1) AS indx, mt.id
FROM my_table mt;
BEGIN
DECLARE v_random_index INTEGER;
DECLARE v_random_id INTEGER;
SELECT ROUND((RAND() * (MAX(indx)-MIN(indx)))+MIN(indx)) INTO v_random_index
FROM temp;
SELECT id INTO v_random_id
FROM temp
WHERE indx=v_random_index;
SELECT *
FROM my_table mt
WHERE id=v_random_id;
END;
END;
I have to tables as:
table1:
UID | COLLEGE_NAME | COLLEGE_ADDRESS
------------------------------------
table2:
UID | COMPANY_NAME | COMPANY_ADDRESS
------------------------------------
i have 2 queries:
select * from table1 where uid='$uid';
select * from table2 where uid='$uid';
i want to write this two queries in one procedure.
structure for multiple select quires in single procedure:
CREATE PROCEDURE sample(l_uid INT) BEGIN
SELECT * FROM college_edu WHERE uid= l_uid;
SELECT * FROM work_experience WHERE uid= l_id;
END
Here's the statement to create STORED PROCEDURE.
DELIMITER $$
CREATE PROCEDURE procedureName(IN _uid VARCHAR(15))
BEGIN
SELECT UID, COLLEGE_NAME name, COLLEGE_ADDRESS address
FROM table1
WHERE uid = _uid
UNION ALL
SELECT UID, COMPANY_NAME name, COMPANY_ADDRESS address
FROM table2
WHERE uid = _uid
END $$
DELIMITER ;
notice that UNION has ALL keyword on it to add duplicate records on the result list. But if you prefer UNIQUE, remove the ALL keyword on the UNION.
below code may serve you purpose. Also the additional tbl column will let you know from which table your data is coming and it would help in further manipulation.
SELECT UID, COLLEGE_NAME name, COLLEGE_ADDRESS address , 1 as tbl
FROM table1
WHERE uid = _uid
UNION ALL
SELECT UID, COMPANY_NAME name, COMPANY_ADDRESS address , 2 as tbl
FROM table2
WHERE uid = _uid
I have the following query where I would like to return two result sets. One being the results as a table and the second just returning the number of potential results, or MaxResults.
The last line errors with Invalid object name ResultsTemp. It does not work until I comment out the second last line. It appears I can only use the ResultsTemp once.
DECLARE #StartRow int;
DECLARE #MaxRows int;
set #StartRow = 0;
set #MaxRows = 5;
WITH ResultsTemp AS
(
SELECT ROW_NUMBER() OVER (ORDER BY FTS.RANK DESC) AS RowId,
Id, Name FROM tNews
INNER JOIN CONTAINSTABLE(tNews, *, 'FORMSOF(INFLECTIONAL, hello)')
AS FTS ON tNews.Id = FTS.[KEY]
)
SELECT Id, Name, RowId FROM ResultsTemp
Group By Id, Name, RowId
Having RowId between #StartRow and (#StartRow + #MaxRows);
select COUNT(*) from ResultsTemp;
thank you
This can't be done (as far as I'm aware). There's three workarounds, one which allows you to keep as two distinct result sets, and the other two which require merging the results into the same result set (as either an extra row or an extra column).
Instead of a CTE, push the results into a temporary table then query from that.
Merge the count into the actual result set as another row: use a UNION ALL and give the count row suitable values for ID, Name and RowID so you can extract it from the rest of the data
Add the SELECT COUNT(*) into the primary result set as an extra column with a CROSS JOIN or similar.
For this latter, you can do it by changing your primary query to:
SELECT Id, Name, RowId, countTable.totalRows
FROM ResultsTemp
CROSS JOIN (SELECT COUNT(*) AS totalRows FROM ResultsTemp) AS countTable
GROUP BY Id, Name, RowId
HAVING RowId BETWEEN #StartRow and (#StartRow + #MaxRows);
I can't vouch for the performance - you'd need to profile.
Create table #temp
(
Id int,
Name Varchar(100),
RowId int
)
DECLARE #StartRow int;
DECLARE #MaxRows int;
set #StartRow = 0;
set #MaxRows = 5;
SELECT ROW_NUMBER() OVER (ORDER BY FTS.RANK DESC) AS RowId,
Id, Name into #temp FROM tNews
INNER JOIN CONTAINSTABLE(tNews, *, 'FORMSOF(INFLECTIONAL, hello)')
AS FTS ON tNews.Id = FTS.[KEY]
SELECT Id, Name, RowId FROM #temp
Group By Id, Name, RowId
Having RowId between #StartRow and (#StartRow + #MaxRows);
select COUNT(RowId) from #temp;
I am currently using the below merge code to migrate date from source to target. I have a new requirement to extend the below code to delete the record from source once an update/insert is performed on the target. Is this possible using merge(all the examples i see on the net had performing del/insert/update in the target not on the source)
MERGE Target1 AS T
USING Source1 AS S
ON (T.EmployeeID = S.EmployeeID)
WHEN NOT MATCHED BY TARGET AND S.EmployeeName LIKE 'S%'
THEN INSERT(EmployeeID, EmployeeName) VALUES(S.EmployeeID, S.EmployeeName)
WHEN MATCHED
THEN UPDATE SET T.EmployeeName = S.EmployeeName
WHEN NOT MATCHED BY SOURCE AND T.EmployeeName LIKE 'S%'
THEN DELETE ;
You can use the output clause to capture the modified/inserted rows to a table variable and use that with a delete statement after the merge.
DECLARE #T TABLE(EmployeeID INT);
MERGE Target1 AS T
USING Source1 AS S
ON (T.EmployeeID = S.EmployeeID)
WHEN NOT MATCHED BY TARGET AND S.EmployeeName LIKE 'S%'
THEN INSERT(EmployeeID, EmployeeName) VALUES(S.EmployeeID, S.EmployeeName)
WHEN MATCHED
THEN UPDATE SET T.EmployeeName = S.EmployeeName
WHEN NOT MATCHED BY SOURCE AND T.EmployeeName LIKE 'S%'
THEN DELETE
OUTPUT S.EmployeeID INTO #T;
DELETE Source1
WHERE EmployeeID in (SELECT EmployeeID
FROM #T);
Nice Reponse, but your code will delete the row from your destination table, here's an exemple in wich you can delete the rows from your source destination without affecting your target table :
if OBJECT_ID('audit.tmp1') IS NOT NULL
DROP TABLE audit.tmp1
select *
into audit.tmp1
from
(
select 1 id, 'aa' nom, convert(date,'2014-01-01') as dd UNION ALL
select 2 id, 'bb' nom, convert(date,'2013-07-12') as dd UNION ALL
select 3 id, 'cc' nom, convert(date,'2012-08-21') as dd UNION ALL
select 4 id, 'dd' nom, convert(date,'2011-11-15') as dd UNION ALL
select 5 id, 'ee' nom, convert(date,'2010-05-16') as dd ) T
if OBJECT_ID('audit.tmp2') IS NOT NULL
DROP TABLE audit.tmp2
select *
into audit.tmp2
from
(
select 1 id, 'aAa' nom, convert(date,'2014-01-14') as dd UNION ALL
select 2 id, 'bbB' nom, convert(date,'2013-06-13') as dd UNION ALL
select 4 id, 'dDD' nom, convert(date,'2012-11-05') as dd UNION ALL
select 6 id, 'FFf' nom, convert(date,'2014-01-12') as dd) T
SELECT * FROM audit.tmp1 order by 1
SELECT * FROM audit.tmp2 order by 1
DECLARE #T TABLE(ID INT);
MERGE audit.tmp2 WITH (HOLDLOCK) AS T
USING (SELECT * FROM audit.tmp1 WHERE nom <> 'dd') AS S
ON (T.id = S.id)
WHEN NOT MATCHED BY TARGET
THEN INSERT(id, nom, dd) VALUES(S.id, S.nom, S.dd)
WHEN MATCHED
THEN UPDATE SET T.nom = S.nom, T.dd = S.dd
WHEN NOT MATCHED BY SOURCE
THEN UPDATE SET T.id = T.id OUTPUT S.id INTO #T;
DELETE tmp1
FROM audit.tmp1
INNER JOIN
#T AS DEL
ON DEL.id = tmp1 .id
SELECT * FROM audit.tmp1 ORDER BY 1
SELECT * FROM audit.tmp2 ORDER BY 1
I hope this will help you.
In our case, we wanted to use MERGE to synchronize our internal database with an outside source of a different structure. Automated CASCADE settings were not an option because we enjoy many cyclical relationships and, really, we don't like that kind of cheap power in the hands of disgruntled staffers. We can't delete parent rows before their child rows are gone.
All of this is done with lightning fast MERGEs that use Table Value Parameters. They provide, by far, the best performance with obscenely low app memory overhead.
Combining scattered advice for the MERGE of Orders data...
CREATE PROCEDURE MyOrderMerge #SourceValues [MyOrderSqlUserType] READONLY
AS
BEGIN
DECLARE #LiveRows TABLE (MergeAction VARCHAR(20), OrderId INT);
DECLARE #DeleteCount INT;
SET #DeleteCount = 0;
MERGE INTO [Order] AS [target]
USING ( SELECT sv.OrderNumber,
c.CustomerId,
st.ShipTypeId
sv.OrderDate,
sv.IsPriority
FROM #SourceValues sv
JOIN [Customer] c ON sv.[CustomerName] = c.[CustomerName]
JOIN [ShipType] st ON ...
) AS [stream]
ON [stream].[OrderNumber] = [target].[SourceOrderNumber]
WHEN MATCHED THEN
UPDATE
...
WHEN NOT MATCHED BY TARGET THEN
INSERT
---
-- Keep a tally of all active source records
-- SQL Server's "INSERTED." prefix encompases both INSERTed and UPDATEd rows <insert very bad words here>
OUTPUT $action, INSERTED.[OrderId] INTO #LiveRows
; -- MERGE has ended
-- Delete child OrderItem rows before parent Order rows
DELETE FROM [OrderItem]
FROM [OrderItem] oi
-- Delete the Order Items that no longer exist at the source
LEFT JOIN #LiveRows lr ON oi.[OrderId] = lr.[OrderId]
WHERE lr.OrderId IS NULL
;
SET #DeleteCount = #DeleteCount + ##ROWCOUNT;
-- Delete parent Order rows that no longer have child Order Item rows
DELETE FROM [Order]
FROM [Order] o
-- Delete the Orders that no longer exist at the source
LEFT JOIN #LiveRows lr ON o.[OrderId] = lr.[OrderId]
WHERE lr.OrderId IS NULL
;
SET #DeleteCount = #DeleteCount + ##ROWCOUNT;
SELECT MergeAction, COUNT(*) AS ActionCount FROM #LiveRows GROUP BY MergeAction
UNION
SELECT 'DELETE' AS MergeAction, #DeleteCount AS ActionCount
;
END
Everything is done in one sweet loop-dee-loop streamed round trip and highly optimized on key indexes. Even though internal primary key values are unknown from the source, the MERGE operation makes them available to the DELETE operations.
The Customer MERGE uses a different #LiveRows TABLE structure, consequentially a different OUTPUT statement and different DELETE statements...
CREATE PROCEDURE MyCustomerMerge #SourceValues [MyCustomerSqlUserType] READONLY
AS
BEGIN
DECLARE #LiveRows TABLE (MergeAction VARCHAR(20), CustomerId INT);
DECLARE #DeleteCount INT;
SET #DeleteCount = 0;
MERGE INTO [Customer] AS [target]
...
OUTPUT $action, INSERTED.[CustomerId] INTO #LiveRows
; -- MERGE has ended
-- Delete child OrderItem rows before parent Order rows
DELETE FROM [OrderItem]
FROM [OrderItem] oi
JOIN [Order] o ON oi.[OrderId] = o.[OrderId]
-- Delete the Order Items that no longer exist at the source
LEFT JOIN #LiveRows lr ON o.[CustomerId] = lr.[CustomerId]
WHERE lr.CustomerId IS NULL
;
SET #DeleteCount = #DeleteCount + ##ROWCOUNT;
-- Delete child Order rows before parent Customer rows
DELETE FROM [Order]
FROM [Order] o
-- Delete the Orders that no longer exist at the source
LEFT JOIN #LiveRows lr ON o.[CustomerId] = lr.[CustomerId]
WHERE lr.CustomerId IS NULL
;
SET #DeleteCount = #DeleteCount + ##ROWCOUNT;
-- Delete parent Customer rows that no longer have child Order or grandchild Order Item rows
DELETE FROM [Customer]
FROM [Customer] c
-- Delete the Customers that no longer exist at the source
LEFT JOIN #LiveRows lr ON c.[CustomerId] = lr.[CustomerId]
WHERE lr.CustomerId IS NULL
;
SET #DeleteCount = #DeleteCount + ##ROWCOUNT;
SELECT MergeAction, COUNT(*) AS ActionCount FROM #LiveRows GROUP BY MergeAction
UNION
SELECT 'DELETE' AS MergeAction, #DeleteCount AS ActionCount
;
END
Setup and maintenance is a bit of a pain - but so worth the efficiencies reaped.
you can also use below code
drop table energydata
create table temp_energydata
(
webmeterID int,
DT DateTime ,
kWh varchar(10)
)
Insert into temp_energydata
select 1,getdate()-10, 120
union
select 2,getdate()-9, 140
union
select 3,getdate()-6, 37
union
select 4,getdate()-3, 40
union
select 5,getdate()-1, 240
create table energydata
(
webmeterID int,
DT DateTime ,
kWh varchar(10)
)
Insert into energydata (webmeterID,kWh)
select 1, 120
union
select 2, 140
union
select 3, 37
union
select 4, 40
select * from energydata
select * from temp_energydata
begin tran ABC
DECLARE #T TABLE(ID INT);
MERGE INTO dbo.energydata WITH (HOLDLOCK) AS target
USING dbo.temp_energydata AS source
ON target.webmeterID = source.webmeterID
AND target.kWh = source.kWh
WHEN MATCHED THEN
UPDATE SET target.DT = source.DT
WHEN NOT MATCHED BY source THEN delete
OUTPUT source.webmeterID INTO #T;
DELETE temp_energydata
WHERE webmeterID in (SELECT webmeterID
FROM #T);
--INSERT (webmeterID, DT, kWh)
--VALUES (source.webmeterID, source.DT, source.kWh)
rollback tran ABC
commit tran ABC