Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
Input
TABLE 1: Header
ID ITEMNAME COST
1 MIXEE 1800
2 REFRIZIRATOR 12000
TABLE 2 : DETAILS
DID DITEMNAME DLINE DCOST
1 MIXEE 1 900
2 MIXEE 2 900
3 REFRIGIRATOR 1 9000
4 REFRIGIRATOR 2 1000
Expected OutPut
DETAILS
DID DITEMNAME DLINE DCOST
1 MIXEE 1 900
2 MIXEE 2 900
3 REFRIGIRATOR 1 12000
4 REFRIGIRATOR 2 0
Explanation : Header table will contains summary sum or line level information or it contains the accurate information and detail table may not contain accurate information cost
so if header table cost and sum of detail table cost is same then i need to keep the records as it is other wise i need to make first line level value is cost of header table and next line should be zero .
Like this i do have around 80 lacs records in detail table .
Okay so I think I got it. Let me know if it needs any tweaks.
Your Tables
DECLARE #Header TABLE (ID INT, ItemName VARCHAR(20), Cost INT);
DECLARE #Details TABLE (ID INT, DiteName VARCHAR(20), Dline TINYINT,DCost INT);
INSERT INTO #Header
VALUES (1,'Mixee',1800),
(2,'Refridgerator',12000);
INSERT INTO #Details
VALUES (1,'Mixee',1,900),
(2,'Mixee',2,900),
(3,'Refridgerator',1,9000),
(4,'Refridgerator',2,9000);
Actual Query
SELECT D.ID,
D.DiteName,
D.Dline,
CASE
WHEN H.Cost = sum_DCost THEN DCost
ELSE CASE
WHEN D.ID = CA.min_ID THEN H.Cost
ELSE 0
END
END AS DCost
FROM #Details AS D
CROSS APPLY (
SELECT DiteName,SUM(DCost) sum_DCost,MIN(ID) min_ID
FROM #Details
WHERE DiteName = D.DiteName
GROUP BY DiteName
) CA
INNER JOIN #Header AS H
ON D.DiteName = H.ItemName
Here is how you can do it:
SELECT d.ID, d.DITEMNAME, d.DLINE,
CASE WHEN SUM(d.DCOST) OVER(PARTITION BY d.DITEMNAME) = h.COST
THEN d.DCOST
ELSE CASE WHEN d.DLINE = 1 THEN h.COST ELSE 0 END END AS DCOST
FROM Header h
JOIN Details d ON h.ITEMNAME = d.DITEMNAME
Related
I want my Firebird SQL to loop through part of the code WHILE a condition is meet.
Initially I didn't even think it was possible. However I have done some reading and now believe that I can use WHILE loop.
I understand a FOR loop is not what I want as it applies to the whole code, not just part of it.
I am using this in Excel and could use some VBA code to do what I want, but it would be better if I can do it all via Firebird SQL as then I can apply it elsewhere.
SELECT
'1' as "Qty",
'of ' || ALP3.PROPERTYVALUE AS "Total Qty"
FROM ASSEMBLYLINES
LEFT JOIN ASSEMBLYLINEPROPS ALP1 ON ALP1.HEADERSYSUNIQUEID = ASSEMBLYLINES.SYSUNIQUEID AND ALP1.PROPERTYNAME = 'Process2'
LEFT JOIN ASSEMBLYLINEPROPS ALP2 ON ALP2.HEADERSYSUNIQUEID = ASSEMBLYLINES.SYSUNIQUEID AND ALP2.PROPERTYNAME = 'Process3'
LEFT JOIN ASSEMBLYLINEPROPS ALP3 ON ALP3.HEADERSYSUNIQUEID = ASSEMBLYLINES.SYSUNIQUEID AND ALP3.PROPERTYNAME = 'Job Quantity'
LEFT JOIN ASSEMBLYLINEPROPS ALP4 ON ALP4.HEADERSYSUNIQUEID = ASSEMBLYLINES.SYSUNIQUEID AND ALP4.PROPERTYNAME = 'Drawing No'
WHERE ASSEMBLYLINES.ORDERNUMBER='16708R01'
AND ASSEMBLYLINES.LINECODE='FABPART'
AND ASSEMBLYLINES.SYSUSERCREATED <> 'EXTERNAL USER'
ORDER BY ALP4.PROPERTYVALUE
My results using the code above is:
Qty Total Qty
1 4
However, what I want is:
My results using the code above is:
Qty Total Qty
1 4
2 4
3 4
4 4
I understand the While loop would be something like:
While Qty <= ALP3.PROPERTYVALUE Do
<<output>>
Loop
Qty Total Qty
1 4
2 4
3 4
4 4
I understand the While loop would be something like:
While Qty <= ALP3.PROPERTYVALUE Do
<<output>>
Loop
So, your "quantity" column is not actually a quantity of some real data (like quantity of containers in cargo ship), but a row number in some your output report/grid.
And then what you want is limiting the output "rowset" - matrix, table, grid - to some N first rows.
Well, that is exactly how it is done, asking for the first rows only.
Select FIRST(4) column1, column2, column3
From table 1
Where condition1 and condition2 or condition3
See the "first" clause in documentation: https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-dml-select.html
Also see "Limiting result rows" chapyer in Wikipedia: https://en.wikipedia.org/wiki/Select_%28SQL%29#Limiting_result_rows
You can also use "window functions" starting with Firebird version 3, but they are somewhat overkill for the simple task of "only give me first N rows".
Now, there is one more method that provides for embedding a totally voluntary condition, but that is from "ugly hacks" toolsets and does not work in a typical situation when several simultaneous connections form different client programs are running. You can use a "generator" as part of the WHILE clause:
Select .....
Where (GEN_ID(cancel_generator_name, 0) = 0) AND ( ...you normal conditions...)
You set the generator value to 0 before the query, and your client evaluates some conditions of your choice while reading the data, and when it wants to - from some another SQL command library object it issues the generator change command, which would immediately skip the rest of the query. However while sometimes this is a useful technique, but only in very specific rare situations.
Since Mark seems to be better guessing than me, then some outlines for future guesswork.
SP is a standard abbreviation for SQL Stored Procedure. Firebird's Execute Block is essentially an anonymous non-persistent SP.
So, we start with a persistent and named SP.
create or alter procedure SEQ (
FROM_1_TO integer not null)
returns (
COUNTER integer)
as
begin
counter = 1;
while ( counter <= from_1_to ) do begin
suspend;
counter = counter + 1;
end
end
Select 1, s.counter from rdb$database, seq(5) s
CONSTANT COUNTER
1 1
1 2
1 3
1 4
1 5
The next question would be how to
join the table with SP (stored procedure) dependent upon specific table row values
avoid SP being executed with NULL parameter values
The answer is - LEFT JOIN, as shown in the FAQ: http://www.firebirdfaq.org/faq143/
CREATE TABLE T2 (
ID INTEGER NOT NULL PRIMARY KEY,
TITLE VARCHAR(10) NOT NULL,
QTY INTEGER NOT NULL
);
INSERT INTO T2 (ID, TITLE, QTY) VALUES (1, 'aaaa', 2);
INSERT INTO T2 (ID, TITLE, QTY) VALUES (2, 'bbbb', 5);
INSERT INTO T2 (ID, TITLE, QTY) VALUES (3, 'ccccc', 4);
Select * from t2 t
left join seq(t.qty) s on 1=1
ID TITLE QTY COUNTER
1 aaaa 2 1
1 aaaa 2 2
2 bbbb 5 1
2 bbbb 5 2
2 bbbb 5 3
2 bbbb 5 4
2 bbbb 5 5
3 ccccc 4 1
3 ccccc 4 2
3 ccccc 4 3
3 ccccc 4 4
If you would have many different queries on different tables/fields that would require this rows-cloning added then having a dedicated counter-generating SP makes sense.
However if you only need this rather exotic rows cloning once, then maybe polluting a global namespace with an SP you would never need again would be less of a good idea.
It seems one can not select from an EB, though: Select from execute block?
So you would have to make a specific ad-hoc EB exactly for your select statement. Which, arguably, might be the very reason d'etre for anonymous non-persistent EB.
execute block
returns (ID INTEGER, TITLE VARCHAR(10), QTY INTEGER, COUNTER INTEGER)
as
begin
for select
id, title, qty from t2
into :id, :title, :qty
do begin
counter = 1;
while
(counter <= qty)
do begin
suspend;
counter = counter + 1;
end
end
end
However the data access library your application uses to connect to Firebird should understand then that while this query is not SELECT-query it still returns the "rowset". Usually they do, but who knows.
I want to select top 5 rows where area is 'newyork' and select any records if no records available or select other 2 if only 3 records found for area 'newyork' then select remaining records accordingly. Example select * from tablename where area = 'newyork' fetch top 5 rows only [If only 3 reocords exists then select remaining 2 rows with other available area]. Can someone please help with this?
Basically, you want the 'newyork' to be sorted first, here's one way.
select
case when area = 'newyork' then 0 else 1 end as nyFlag
<whatever else>
from mytable
order by 1, <....>
fetch first 5 rows only
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
How to Select 5 row even if there is 3 row of data and next 2 row should be empty row.
You can union the result with empty rows, which should have the same types of columns as the table. Example:
with my_table(id, str, a_date) as (
values
(1, 'first', '2017-09-01'::date),
(2, 'second', '2017-09-02'),
(3, 'third', '2017-09-03')
)
select *
from my_table
union all
select null::int, null::text, null::date
from generate_series(1, 5) -- generate 5 empty rows
order by id nulls last
limit 5
id | str | a_date
----+--------+------------
1 | first | 2017-09-01
2 | second | 2017-09-02
3 | third | 2017-09-03
| |
| |
(5 rows)
You need a table with 5 numbers, then you assign a row number to your table and perform a left join.
The cte are the data, then at the end is the query need it:
SQL DEMO
WITH all5 as (
SELECT *
FROM (VALUES(1), (2), (3), (4), (5)) AS t (ID)
), some2 as (
SELECT *
FROM (VALUES('first'), ('second')) AS t (value)
)
SELECT *
FROM all5
LEFT JOIN (SELECT row_number() OVER (ORDER BY value) as rn, *
FROM some2) as t
ON all5.ID = t.rn
OUTPUT
Assume we have a table and we want to do a sum of the Expend column so that the summation only adds up values of the same Week_Name.
SN Week_Name Exp Sum
-- --------- --- ---
1 Week 1 10 0
2 Week 1 20 0
3 Week 1 30 60
4 Week 2 40 0
5 Week 2 50 90
6 Week 3 10 0
I will assume we will need to `Order By' Week_Name, then compare the previous Week_Name(previous row) with the current row Week_name(Current row).
If both are the same, put zero in the SUM column.
If not the same, add all expenditure, where Week_Name = Week_Name(Previous row) and place in the Sum column. The final output should look like the table above.
Any help on how to achieve this in T-SQL is highly appreciated.
Okay, I was eventually able to resolve this issue, praise Jesus! If you want the exact table I gave above, you can use GilM's response below, it is perfect. If you want your table to have running Cumulatives, i.e. Rows 3 shoud have 60, Row 5, should have 150, Row 6 160 etc. Then, you can use my code below:
USE CAPdb
IF OBJECT_ID ('dbo.[tablebp]') IS NOT NULL
DROP TABLE [tablebp]
GO
CREATE TABLE [tablebp] (
tablebpcCol1 int PRIMARY KEY
,tabledatekey datetime
,tableweekname varchar(50)
,expenditure1 numeric
,expenditure_Cummulative numeric
)
INSERT INTO [tablebp](tablebpcCol1,tabledatekey,tableweekname,expenditure1,expenditure_Cummulative)
SELECT b.s_tablekey,d.PK_Date,d.Week_Name,
SUM(b.s_expenditure1) AS s_expenditure1,
SUM(b.s_expenditure1) + COALESCE((SELECT SUM(s_expenditure1)
FROM source_table bs JOIN dbo.Time dd ON bs.[DATE Key] = dd.[PK_Date]
WHERE dd.PK_Date < d.PK_Date),0)
FROM source_table b
INNER JOIN dbo.Time d ON b.[Date key] = d.PK_Date
GROUP BY d.[PK_Date],d.Week_Name,b.s_tablekey,b.s_expenditure1
ORDER BY d.[PK_Date]
;WITH CTE AS (
SELECT tableweekname
,Max(expenditure_Cummulative) AS Week_expenditure_Cummulative
,MAX(tablebpcCol1) AS MaxSN
FROM [tablebp]
GROUP BY tableweekname
)
SELECT [tablebp].*
,CASE WHEN [tablebp].tablebpcCol1 = CTE.MaxSN THEN Week_expenditure_Cummulative
ELSE 0 END AS [RunWeeklySum]
FROM [tablebp]
JOIN CTE on CTE.tableweekname = [tablebp].tableweekname
I'm not sure why your SN=6 line is 0 rather than 10. Do you really not want the sum for the last Week? If having the last week total is okay, then you might want something like:
;WITH CTE AS (
SELECT Week_Name,SUM([Expend.]) as SumExpend
,MAX(SN) AS MaxSN
FROM T
GROUP BY Week_Name
)
SELECT T.*,CASE WHEN T.SN = CTE.MaxSN THEN SumExpend
ELSE 0 END AS [Sum]
FROM T
JOIN CTE on CTE.Week_Name = T.Week_Name
Based on the requst in the comment wanting a running total in SUM you could try this:
;WITH CTE AS (
SELECT Week_Name, MAX(SN) AS MaxSN
FROM T
GROUP BY Week_Name
)
SELECT T.SN, T.Week_Name,T.Exp,
CASE WHEN T.SN = CTE.MaxSN THEN
(SELECT SUM(EXP) FROM T T2
WHERE T2.SN <= T.SN) ELSE 0 END AS [SUM]
FROM T
JOIN CTE ON CTE.Week_Name = T.Week_Name
ORDER BY SN
I need to be able to query a SharePoint database for survey results. The type of data I'm having problems with is a "Rating Scale" value. So the data in each table column represents a whole group of sub-questions and their answers.
So the following is an example of what is found in ONE column:
1. Our function has defined how Availability is measured the hardware/software in Production;#3#2. Availability threshold levels exist for our function (e.g., SLA's);#3#3. Our function follows a defined process when there are threshold breaches;#4#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#4#6. Operating Level Agreements (OLA's) guide our interaction with other internal teams;#4#
The Questions end with a semi-colon and their answers are inside the two # signs. So the answer to the first question is 3.
When I export the results of the survey it formats each question as a column header and the answer as the value in the cell below, which is ideal to get an average for each question, and would love to be able to replicate that from a SQL query.
But if I could get query results into two columns (Question, Answer)...I'd be thrilled with that.
Any help is appreciated.
Thanks very much
Hank Stallings
*****ADDENDUM:**
This was my version of astander's solution...THANKS again!
DECLARE #Table TABLE(
QuestionSource VARCHAR(50),
QA VARCHAR(5000)
)
DECLARE #ReturnTable TABLE(
QuestionSource VARCHAR(50),
Question VARCHAR(5000),
Answer int
)
DECLARE #XmlField XML,
#QuestionSource VARCHAR(50)
INSERT INTO #Table SELECT
'Availability' AS QuestionSource,CONVERT(varchar(5000),ntext1) FROM UserData WHERE tp_ContentType = 'My Survey'
INSERT INTO #Table SELECT
'Capacity' AS QuestionSource,CONVERT(varchar(5000),ntext2) FROM UserData WHERE tp_ContentType = 'My Survey'
--SELECT * FROM #Table
DECLARE Cur CURSOR FOR
SELECT QuestionSource,
CAST(Val AS XML) XmlVal
FROM (
SELECT QuestionSource,
LEFT(Vals, LEN(Vals) - LEN('<option><q>')) Val
FROM (
SELECT QuestionSource,
'<option><q>' + REPLACE(REPLACE(REPLACE(QA,'&','&'), ';#','</q><a>'), '#', '</a></option><option><q>') Vals
FROM #Table
) sub
) sub
OPEN Cur
FETCH NEXT FROM Cur INTO #QuestionSource,#XmlField
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #ReturnTable
SELECT #QuestionSource,
T.split.query('q').value('.', 'nvarchar(max)') question,
T.split.query('a').value('.', 'nvarchar(max)') answer
FROM #XmlField.nodes('/option') T(split)
FETCH NEXT FROM Cur INTO #QuestionSource,#XmlField
END
CLOSE Cur
DEALLOCATE Cur
SELECT * FROM #ReturnTable
You have to have a split function set up, but once you have it, try this cursor free solution:
I prefer the number table approach to split a string in TSQL
For this method to work, you need to do this one time table setup:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Once the Numbers table is set up, create this split function, which WILL return empty strings and row numbers:
CREATE FUNCTION [dbo].[FN_ListToTableRows]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN
(
----------------
--SINGLE QUERY-- --this WILL return empty rows
----------------
SELECT
ROW_NUMBER() OVER(ORDER BY number) AS RowNumber
,LTRIM(RTRIM(SUBSTRING(ListValue, number+1, CHARINDEX(#SplitOn, ListValue, number+1)-number - 1))) AS ListValue
FROM (
SELECT #SplitOn + #List + #SplitOn AS ListValue
) AS InnerQuery
INNER JOIN Numbers n ON n.Number < LEN(InnerQuery.ListValue)
WHERE SUBSTRING(ListValue, number, 1) = #SplitOn
);
GO
You can now easily split a CSV string into a table and join on it, NOTE this split function returns empty strings and row numbers:
select * from dbo.FN_ListToTableRows(',','1,2,3,,,4,5,6777,,,')
OUTPUT:
RowNumber ListValue
-------------------- ------------
1 1
2 2
3 3
4
5
6 4
7 5
8 6777
9
10
11
(11 row(s) affected)
Your can now use a CROSS APPLY to split every row in your table like:
DECLARE #YourTable table (RowID int, RowValue varchar(8000))
INSERT INTO #YourTable VALUES (1,'1. Our function has defined how Availability is measured the hardware/software in Production;#3#2. Availability threshold levels exist for our function (e.g., SLA''s);#3#3. Our function follows a defined process when there are threshold breaches;#4#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#4#6. Operating Level Agreements (OLA''s) guide our interaction with other internal teams;#4#')
INSERT INTO #YourTable VALUES (2,'1. one;#1#2. two;#2#3. three;#3#')
INSERT INTO #YourTable VALUES (3,'1. aaa;#1#2. bbb;#2#3. ccc;#3#')
;WITH AllRows As
(
SELECT
o.RowID,st.RowNumber,st.ListValue AS RowValue
FROM #YourTable o
CROSS APPLY dbo.FN_ListToTableRows('#',LEFT(o.RowValue,LEN(o.RowValue)-1)) AS st
)
SELECT
a.RowID,a.RowValue AS Question, b.RowValue AS Answer
FROM AllRows a
LEFT OUTER JOIN AllRows b ON a.RowID=b.RowID AND a.RowNumber+1=b.RowNumber
WHERE a.RowNumber % 2 = 1
OUTPUT:
RowID Question Answer
----------- ----------------------------------------------------------------------------------------------- -------
1 1. Our function has defined how Availability is measured the hardware/software in Production; 3
1 2. Availability threshold levels exist for our function (e.g., SLA's); 3
1 3. Our function follows a defined process when there are threshold breaches; 4
1 4. Our function collects and maintains Availability data; 4
1 5. Comparative analysis helps identify trending with the Availability data; 4
1 6. Operating Level Agreements (OLA's) guide our interaction with other internal teams; 4
2 1. one; 1
2 2. two; 2
2 3. three; 3
3 1. aaa; 1
3 2. bbb; 2
3 3. ccc; 3
(12 row(s) affected)
OK, let see. I had to use a cursor, as this would probably have been better achieved from a programming language like C#, but here goes... Using Sql Server 2005, try the following. Let me know if you need any explanations.
DECLARE #Table TABLE(
QuestionSource VARCHAR(50),
QA VARCHAR(1000)
)
DECLARE #ReturnTable TABLE(
QuestionSource VARCHAR(50),
Question VARCHAR(1000),
Answer VARCHAR(10)
)
DECLARE #XmlField XML,
#QuestionSource VARCHAR(40)
INSERT INTO #Table SELECT
'Availability','1. Our function has defined how Availability is measured the hardware/software in Production;#3#2. Availability threshold levels exist for our function (e.g., SLA''s);#3#3. Our function follows a defined process when there are threshold breaches;#4#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#4#6. Operating Level Agreements (OLA''s) guide our interaction with other internal teams;#4#'
INSERT INTO #Table SELECT
'Capacity', '1. Our function has defined how Availability is measured the hardware/software in Production;#1#2. Availability threshold levels exist for our function (e.g., SLA''s);#2#3. Our function follows a defined process when there are threshold breaches;#3#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#5#6. Operating Level Agreements (OLA''s) guide our interaction with other internal teams;#6#'
DECLARE Cur CURSOR FOR
SELECT QuestionSource,
CAST(Val AS XML) XmlVal
FROM (
SELECT QuestionSource,
LEFT(Vals, LEN(Vals) - LEN('<option><q>')) Val
FROM (
SELECT QuestionSource,
'<option><q>' + REPLACE(REPLACE(QA, ';#','</q><a>'), '#', '</a></option><option><q>') Vals
FROM #Table
) sub
) sub
OPEN Cur
FETCH NEXT FROM Cur INTO #QuestionSource, #XmlField
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #ReturnTable
SELECT #QuestionSource,
T.split.query('q').value('.', 'nvarchar(max)') question,
T.split.query('a').value('.', 'nvarchar(max)') answer
FROM #XmlField.nodes('/option') T(split)
FETCH NEXT FROM Cur INTO #QuestionSource, #XmlField
END
CLOSE Cur
DEALLOCATE Cur
SELECT *
FROM #ReturnTable